From 0b278005ad98bcd862bd348d08f664005e9eda60 Mon Sep 17 00:00:00 2001 From: Pamela Dragosh Date: Mon, 28 Oct 2019 08:51:10 -0400 Subject: Optimization improvements and test cases * StdBaseTranslator added helpful support methods and can now add obligations to either rule, policy or policy sets. * StdMatchablePolicyRequest improved to support optional policy-type as part of the request to refine the output results. * Added more tests to ensure that the decision is returning the appropriate results. * Added more Javadoc for code. * Added some sonar fix for either log or throw exception. Issue-ID: POLICY-2066 Change-Id: I90d6d90c2cdbb627e96cfce1d2632b2439a1e477 Signed-off-by: Pamela Dragosh --- .../application/common/std/StdBaseTranslator.java | 155 ++++++++++++++++++++- .../common/std/StdMatchablePolicyRequest.java | 22 ++- .../common/std/StdMatchableTranslator.java | 153 +++++++++++++++----- 3 files changed, 284 insertions(+), 46 deletions(-) (limited to 'applications/common') 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 48da9969..d2d383b9 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 @@ -28,16 +28,23 @@ 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; import oasis.names.tc.xacml._3_0.core.schema.wd_17.ObligationExpressionsType; +import oasis.names.tc.xacml._3_0.core.schema.wd_17.PolicySetType; 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 org.onap.policy.models.decisions.concepts.DecisionRequest; @@ -46,6 +53,7 @@ import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicy; 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; +import org.onap.policy.pdp.xacml.application.common.ToscaPolicyTranslatorUtils; import org.onap.policy.pdp.xacml.application.common.XacmlPolicyUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -99,6 +107,13 @@ public class StdBaseTranslator implements ToscaPolicyTranslator { return decisionResponse; } + /** + * 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. + */ protected void scanObligations(Collection obligations, DecisionResponse decisionResponse) { for (Obligation obligation : obligations) { LOGGER.info("Obligation: {}", obligation); @@ -109,6 +124,12 @@ public class StdBaseTranslator implements ToscaPolicyTranslator { } } + /** + * 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) { // @@ -169,11 +190,21 @@ public class StdBaseTranslator implements ToscaPolicyTranslator { return policy; } - protected RuleType addObligation(RuleType rule, String jsonPolicy) { + /** + * addObligation - general code to add a json policy as an obligation. Probably could just + * return the obligation only instead of adding it directly to a rule/policy/policyset. + * But this is fine for now. + * + * @param RuleType, PolicyType, PolicySetType object + * @param ruleOrPolicy Incoming RuleType, PolicyType, PolicySetType object + * @param jsonPolicy JSON String representation of policy. + * @return Return the Incoming RuleType, PolicyType, PolicySetType object for convenience. + */ + protected T addObligation(T ruleOrPolicy, String jsonPolicy) { // - // Convert the YAML Policy to JSON Object + // Creating obligation for returning policy // - LOGGER.info("JSON Optimization Policy {}{}", XacmlPolicyUtils.LINE_SEPARATOR, jsonPolicy); + LOGGER.info("Obligation Policy {}{}", XacmlPolicyUtils.LINE_SEPARATOR, jsonPolicy); // // Create an AttributeValue for it // @@ -196,12 +227,124 @@ public class StdBaseTranslator implements ToscaPolicyTranslator { obligation.setObligationId(ToscaDictionary.ID_OBLIGATION_REST_BODY.stringValue()); obligation.getAttributeAssignmentExpression().add(expressionType); // - // Now we can add it into the rule + // Now we can add it into the rule/policy/policyset // ObligationExpressionsType obligations = new ObligationExpressionsType(); obligations.getObligationExpression().add(obligation); - rule.setObligationExpressions(obligations); - return rule; + if (ruleOrPolicy instanceof RuleType) { + ((RuleType) ruleOrPolicy).setObligationExpressions(obligations); + } else if (ruleOrPolicy instanceof PolicyType) { + ((PolicyType) ruleOrPolicy).setObligationExpressions(obligations); + } else if (ruleOrPolicy instanceof PolicySetType) { + ((PolicySetType) ruleOrPolicy).setObligationExpressions(obligations); + } + // + // Return as a convenience + // + return ruleOrPolicy; + } + + /** + * generateAnyOfForPolicyType - Creates a specific AnyOfType that includes the check + * to match on a specific TOSCA Policy Type. + * + * @param type String represenatation of TOSCA Policy Type (eg. "onap.policies.Foo") + * @return AnyOfType object + */ + protected AnyOfType generateAnyOfForPolicyType(String type) { + // + // Create the match for the policy type + // + MatchType match = ToscaPolicyTranslatorUtils.buildMatchTypeDesignator( + XACML3.ID_FUNCTION_STRING_EQUAL, + type, + XACML3.ID_DATATYPE_STRING, + ToscaDictionary.ID_RESOURCE_POLICY_TYPE, + XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE); + // + // Add it to an AnyOfType object + // + AnyOfType anyOf = new AnyOfType(); + anyOf.getAllOf().add(ToscaPolicyTranslatorUtils.buildAllOf(match)); + // + // Return new AnyOfType + // + return anyOf; + } + + /** + * generateConditionForPolicyType - create a ConditionType XACML object + * that is able to determine if a request specifies a specific policy type + * that the policy is created from, only then is the rule applied. If the + * request doesn't even care about the policy type (eg it is missing) then + * return the rule should not apply. + * + * @param type PolicyType (eg. onap.policies.Foo + * @return ConditionType object + */ + protected ConditionType generateConditionForPolicyType(String type) { + // + // Create an ApplyType that checks if the request contains the + // policy-type attribute + // + AttributeDesignatorType designator = new AttributeDesignatorType(); + designator.setAttributeId(ToscaDictionary.ID_RESOURCE_POLICY_TYPE.stringValue()); + designator.setCategory(XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE.stringValue()); + designator.setDataType(XACML3.ID_DATATYPE_STRING.stringValue()); + + ApplyType applyBagSize = new ApplyType(); + applyBagSize.setDescription("Get the size of policy-type attributes"); + applyBagSize.setFunctionId(XACML3.ID_FUNCTION_STRING_BAG_SIZE.stringValue()); + + AttributeValueType valueZero = new AttributeValueType(); + 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(); + applyGreaterThan.setDescription("Does the policy-type attribute exist?"); + applyGreaterThan.setFunctionId(XACML3.ID_FUNCTION_INTEGER_EQUAL.stringValue()); + + applyGreaterThan.getExpression().add(factory.createApply(applyBagSize)); + applyGreaterThan.getExpression().add(factory.createAttributeValue(valueZero)); + + // + // Create an apply type that checks the actual value + // + AttributeValueType value = new AttributeValueType(); + value.setDataType(XACML3.ID_DATATYPE_STRING.stringValue()); + value.getContent().add(type); + + // + // Create string-is-in apply - which determines if the policy-type + // is in the request bag of resources for policy-type + // + ApplyType applyIsIn = new ApplyType(); + applyIsIn.setDescription("Is this policy-type in the list?"); + applyIsIn.setFunctionId(XACML3.ID_FUNCTION_STRING_IS_IN.stringValue()); + applyIsIn.getExpression().add(factory.createAttributeValue(value)); + applyIsIn.getExpression().add(factory.createAttributeDesignator(designator)); + + // + // Create our outer apply + // + ApplyType applyOr = new ApplyType(); + applyOr.setDescription("IF exists and is equal"); + applyOr.setFunctionId(XACML3.ID_FUNCTION_OR.stringValue()); + + applyOr.getExpression().add(factory.createApply(applyGreaterThan)); + applyOr.getExpression().add(factory.createApply(applyIsIn)); + + // + // Finally create the condition + // + ConditionType condition = new ConditionType(); + + condition.setExpression(factory.createApply(applyOr)); + + return condition; } } diff --git a/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/std/StdMatchablePolicyRequest.java b/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/std/StdMatchablePolicyRequest.java index 0f3a0338..b478e8c1 100644 --- a/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/std/StdMatchablePolicyRequest.java +++ b/applications/common/src/main/java/org/onap/policy/pdp/xacml/application/common/std/StdMatchablePolicyRequest.java @@ -59,6 +59,8 @@ public class StdMatchablePolicyRequest { private static final Logger LOGGER = LoggerFactory.getLogger(StdMatchablePolicyRequest.class); + public static final String POLICY_TYPE_KEY = "policy-type"; + @XACMLSubject(includeInResults = true) private String onapName; @@ -120,7 +122,7 @@ public class StdMatchablePolicyRequest { try { xacmlRequest = RequestParser.parseRequest(request); } catch (IllegalAccessException | DataTypeException e) { - throw new XacmlApplicationException("Could not parse request " + e.getLocalizedMessage()); + throw new XacmlApplicationException("Could not parse request ", e); } // // Create an object we can add to @@ -133,6 +135,15 @@ public class StdMatchablePolicyRequest { // Map resources = decisionRequest.getResource(); for (Entry entrySet : resources.entrySet()) { + // + // Check for special policy-type + // + String attributeId; + if (POLICY_TYPE_KEY.equals(entrySet.getKey())) { + attributeId = ToscaDictionary.ID_RESOURCE_POLICY_TYPE.stringValue(); + } else { + attributeId = ToscaDictionary.ID_RESOURCE_MATCHABLE + entrySet.getKey(); + } // // Making an assumption that these fields are matchable. // Its possible we may have to load the policy type model @@ -140,12 +151,12 @@ public class StdMatchablePolicyRequest { // try { if (entrySet.getValue() instanceof Collection) { - addResources(resourceAttributes, (Collection) entrySet.getValue(), entrySet.getKey()); + addResources(resourceAttributes, (Collection) entrySet.getValue(), attributeId); } else { - addResources(resourceAttributes, Arrays.asList(entrySet.getValue().toString()), entrySet.getKey()); + addResources(resourceAttributes, Arrays.asList(entrySet.getValue().toString()), attributeId); } } catch (DataTypeException e) { - throw new XacmlApplicationException("Failed to add resource " + e.getLocalizedMessage()); + throw new XacmlApplicationException("Failed to add resource ", e); } } mutableRequest.add(resourceAttributes); @@ -162,7 +173,7 @@ public class StdMatchablePolicyRequest { for (Object value : values) { StdMutableAttribute mutableAttribute = new StdMutableAttribute(); mutableAttribute.setCategory(XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE); - mutableAttribute.setAttributeId(new IdentifierImpl(ToscaDictionary.ID_RESOURCE_MATCHABLE + id)); + mutableAttribute.setAttributeId(new IdentifierImpl(id)); mutableAttribute.setIncludeInResults(true); DataType dataTypeExtended = factory.getDataType(XACML3.ID_DATATYPE_STRING); @@ -175,5 +186,4 @@ public class StdMatchablePolicyRequest { } return attributes; } - } 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 5908f53b..7b47ad14 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 @@ -109,12 +109,12 @@ public class StdMatchableTranslator extends StdBaseTranslator { // // Get the TOSCA Policy Type for this policy // - Collection policyTypes = this.getPolicyTypes(toscaPolicy.getTypeIdentifier()); + Collection toscaPolicyTypes = this.getPolicyTypes(toscaPolicy.getTypeIdentifier()); // - // If we don't have any policy types, then we cannot know + // If we don't have any TOSCA policy types, then we cannot know // which properties are matchable. // - if (policyTypes.isEmpty()) { + if (toscaPolicyTypes.isEmpty()) { throw new ToscaPolicyConversionException( "Cannot retrieve Policy Type definition for policy " + toscaPolicy.getName()); } @@ -134,7 +134,7 @@ public class StdMatchableTranslator extends StdBaseTranslator { // // There should be a metadata section // - this.fillMetadataSection(newPolicyType, toscaPolicy.getMetadata()); + fillMetadataSection(newPolicyType, toscaPolicy.getMetadata()); // // Set the combining rule // @@ -142,17 +142,7 @@ public class StdMatchableTranslator extends StdBaseTranslator { // // Generate the TargetType // - newPolicyType.setTarget(generateTargetType(toscaPolicy.getProperties(), policyTypes)); - // - // Now create the Permit Rule - // No target since the policy has a target - // With obligations. - // - RuleType rule = new RuleType(); - rule.setDescription("Default is to PERMIT if the policy matches."); - rule.setRuleId(policyName + ":rule"); - rule.setEffect(EffectType.PERMIT); - rule.setTarget(new TargetType()); + newPolicyType.setTarget(new TargetType()); // // Now represent the policy as Json // @@ -163,13 +153,41 @@ public class StdMatchableTranslator extends StdBaseTranslator { } catch (CoderException e) { throw new ToscaPolicyConversionException("Failed to encode policy to json", e); } - addObligation(rule, jsonPolicy); + // + // Add it as an obligation + // + addObligation(newPolicyType, jsonPolicy); + // + // Now create the Permit Rule for all the + // matchable properties. + // + RuleType rule = new RuleType(); + rule.setDescription("Default is to PERMIT if the policy matches."); + rule.setRuleId(policyName + ":rule"); + rule.setEffect(EffectType.PERMIT); + rule.setTarget(generateTargetType(toscaPolicy.getProperties(), toscaPolicyTypes)); + rule.setCondition(generateConditionForPolicyType(toscaPolicy.getType())); + // + // Add the rule to the policy + // + newPolicyType.getCombinerParametersOrRuleCombinerParametersOrVariableDefinition().add(rule); + // + // If a Decision is for a specific policy-type, make sure + // there is a rule for it. + // + rule = new RuleType(); + rule.setDescription("Match on policy-type " + toscaPolicy.getType()); + rule.setRuleId(policyName + ":rule:policy-type"); + rule.setEffect(EffectType.PERMIT); + TargetType target = new TargetType(); + target.getAnyOf().add(this.generateAnyOfForPolicyType(toscaPolicy.getType())); + rule.setTarget(target); // // Add the rule to the policy // newPolicyType.getCombinerParametersOrRuleCombinerParametersOrVariableDefinition().add(rule); // - // Return our new policy + // Log output of the policy // try (ByteArrayOutputStream os = new ByteArrayOutputStream()) { XACMLPolicyWriter.writePolicyFile(os, newPolicyType); @@ -177,18 +195,20 @@ public class StdMatchableTranslator extends StdBaseTranslator { } catch (IOException e) { LOGGER.error("Failed to create byte array stream", e); } + // + // Done + // return newPolicyType; } /** - * For generating target type, we are scan for matchable properties + * For generating target type, we scan for matchable properties * and use those to build the policy. * * @param properties Properties section of policy * @param policyTypes Collection of policy Type to find matchable metadata * @return TargetType object */ - @SuppressWarnings("unchecked") protected TargetType generateTargetType(Map properties, Collection policyTypes) { TargetType targetType = new TargetType(); // @@ -200,25 +220,21 @@ public class StdMatchableTranslator extends StdBaseTranslator { // if (isMatchable(entrySet.getKey(), policyTypes)) { LOGGER.info("Found matchable property {}", entrySet.getValue()); - if (entrySet.getValue() instanceof Collection) { - AnyOfType anyOf = generateMatches((Collection) entrySet.getValue(), - new IdentifierImpl(ToscaDictionary.ID_RESOURCE_MATCHABLE + entrySet.getKey())); - if (! anyOf.getAllOf().isEmpty()) { - targetType.getAnyOf().add(anyOf); - } - } else { - AnyOfType anyOf = generateMatches(Arrays.asList(entrySet.getValue()), - new IdentifierImpl(ToscaDictionary.ID_RESOURCE_MATCHABLE + entrySet.getKey())); - if (! anyOf.getAllOf().isEmpty()) { - targetType.getAnyOf().add(anyOf); - } - } + generateMatchable(targetType, entrySet.getKey(), entrySet.getValue()); } } return targetType; } + /** + * isMatchable - Iterates through available TOSCA Policy Types to determine if a property + * should be treated as matchable. + * + * @param propertyName Name of property + * @param policyTypes Collection of TOSCA Policy Types to scan + * @return true if matchable + */ protected boolean isMatchable(String propertyName, Collection policyTypes) { for (ToscaPolicyType policyType : policyTypes) { for (Entry propertiesEntry : policyType.getProperties().entrySet()) { @@ -236,6 +252,42 @@ public class StdMatchableTranslator extends StdBaseTranslator { return false; } + /** + * generateMatchable - Given the object, generates list of MatchType objects and add them + * to the TargetType object. + * + * @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 + */ + @SuppressWarnings("unchecked") + protected TargetType generateMatchable(TargetType targetType, String key, Object value) { + if (value instanceof Collection) { + AnyOfType anyOf = generateMatches((Collection) value, + new IdentifierImpl(ToscaDictionary.ID_RESOURCE_MATCHABLE + key)); + if (! anyOf.getAllOf().isEmpty()) { + targetType.getAnyOf().add(anyOf); + } + } else { + AnyOfType anyOf = generateMatches(Arrays.asList(value), + new IdentifierImpl(ToscaDictionary.ID_RESOURCE_MATCHABLE + key)); + if (! anyOf.getAllOf().isEmpty()) { + targetType.getAnyOf().add(anyOf); + } + } + return targetType; + } + + /** + * generateMatches - Goes through the collection of objects, creates a MatchType object + * for each object and associates it with the given attribute Id. Returns the AnyOfType + * object that contains all the generated MatchType objects. + * + * @param matchables Collection of object to generate MatchType from + * @param attributeId Given attribute Id for each MatchType + * @return AnyOfType object + */ protected AnyOfType generateMatches(Collection matchables, Identifier attributeId) { // // This is our outer AnyOf - which is an OR @@ -346,11 +398,17 @@ public class StdMatchableTranslator extends StdBaseTranslator { // childPolicyType = parentPolicyType; } - - return listTypes; } + /** + * findPolicyType - given the ToscaPolicyTypeIdentifier, finds it in memory, or + * then tries to find it either locally on disk or pull it from the Policy + * Lifecycle API the given TOSCA Policy Type. + * + * @param policyTypeId ToscaPolicyTypeIdentifier to find + * @return ToscaPolicyType object. Can be null if failure. + */ private ToscaPolicyType findPolicyType(ToscaPolicyTypeIdentifier policyTypeId) { // // Is it loaded in memory? @@ -368,6 +426,14 @@ public class StdMatchableTranslator extends StdBaseTranslator { return policyType; } + /** + * loadPolicyType - Tries to load the given ToscaPolicyTypeIdentifier from local + * storage. If it does not exist, will then attempt to pull from Policy Lifecycle + * API. + * + * @param policyTypeId ToscaPolicyTypeIdentifier input + * @return ToscaPolicyType object. Null if failure. + */ private ToscaPolicyType loadPolicyType(ToscaPolicyTypeIdentifier policyTypeId) { // // Construct what the file name should be @@ -392,6 +458,10 @@ public class StdMatchableTranslator extends StdBaseTranslator { // return this.pullPolicyType(policyTypeId, policyTypePath); } + // + // Success - we have read locally the policy type. Now bring it into our + // return object. + // LOGGER.info("Read in local policy type {}", policyTypePath.toAbsolutePath()); try { ToscaServiceTemplate serviceTemplate = standardYamlCoder.decode(new String(bytes, StandardCharsets.UTF_8), @@ -432,6 +502,14 @@ public class StdMatchableTranslator extends StdBaseTranslator { return null; } + /** + * pullPolicyType - pulls the given ToscaPolicyTypeIdentifier from the Policy Lifecycle API. + * If successful, will store it locally given the policyTypePath. + * + * @param policyTypeId ToscaPolicyTypeIdentifier + * @param policyTypePath Path object to store locally + * @return ToscaPolicyType object. Null if failure. + */ private synchronized ToscaPolicyType pullPolicyType(ToscaPolicyTypeIdentifier policyTypeId, Path policyTypePath) { // // This is what we return @@ -460,6 +538,13 @@ public class StdMatchableTranslator extends StdBaseTranslator { return policyType; } + /** + * constructLocalFilePath - common method to ensure the name of the local file for the + * policy type is the same. + * + * @param policyTypeId ToscaPolicyTypeIdentifier + * @return Path object + */ private Path constructLocalFilePath(ToscaPolicyTypeIdentifier policyTypeId) { return Paths.get(this.pathForData.toAbsolutePath().toString(), policyTypeId.getName() + "-" + policyTypeId.getVersion() + ".yaml"); -- cgit 1.2.3-korg