2  * ============LICENSE_START=======================================================
 
   4  * ================================================================================
 
   5  * Copyright (C) 2019-2020 AT&T Intellectual Property. All rights reserved.
 
   6  * ================================================================================
 
   7  * Licensed under the Apache License, Version 2.0 (the "License");
 
   8  * you may not use this file except in compliance with the License.
 
   9  * You may obtain a copy of the License at
 
  11  *      http://www.apache.org/licenses/LICENSE-2.0
 
  13  * Unless required by applicable law or agreed to in writing, software
 
  14  * distributed under the License is distributed on an "AS IS" BASIS,
 
  15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 
  16  * See the License for the specific language governing permissions and
 
  17  * limitations under the License.
 
  19  * SPDX-License-Identifier: Apache-2.0
 
  20  * ============LICENSE_END=========================================================
 
  23 package org.onap.policy.pdp.xacml.application.common.std;
 
  25 import com.att.research.xacml.api.Identifier;
 
  26 import com.att.research.xacml.api.Obligation;
 
  27 import com.att.research.xacml.api.Request;
 
  28 import com.att.research.xacml.api.XACML3;
 
  29 import com.att.research.xacml.std.IdentifierImpl;
 
  30 import com.att.research.xacml.util.XACMLPolicyWriter;
 
  31 import java.io.ByteArrayOutputStream;
 
  32 import java.io.IOException;
 
  33 import java.nio.charset.StandardCharsets;
 
  34 import java.nio.file.Files;
 
  35 import java.nio.file.Path;
 
  36 import java.nio.file.Paths;
 
  37 import java.util.Arrays;
 
  38 import java.util.Collection;
 
  39 import java.util.HashMap;
 
  40 import java.util.LinkedHashMap;
 
  41 import java.util.LinkedList;
 
  42 import java.util.List;
 
  44 import java.util.Map.Entry;
 
  46 import oasis.names.tc.xacml._3_0.core.schema.wd_17.AnyOfType;
 
  47 import oasis.names.tc.xacml._3_0.core.schema.wd_17.EffectType;
 
  48 import oasis.names.tc.xacml._3_0.core.schema.wd_17.MatchType;
 
  49 import oasis.names.tc.xacml._3_0.core.schema.wd_17.PolicyType;
 
  50 import oasis.names.tc.xacml._3_0.core.schema.wd_17.RuleType;
 
  51 import oasis.names.tc.xacml._3_0.core.schema.wd_17.TargetType;
 
  52 import org.apache.commons.lang3.tuple.Pair;
 
  53 import org.onap.policy.common.endpoints.parameters.RestServerParameters;
 
  54 import org.onap.policy.common.utils.coder.CoderException;
 
  55 import org.onap.policy.common.utils.coder.StandardCoder;
 
  56 import org.onap.policy.common.utils.coder.StandardYamlCoder;
 
  57 import org.onap.policy.models.decisions.concepts.DecisionRequest;
 
  58 import org.onap.policy.models.decisions.concepts.DecisionResponse;
 
  59 import org.onap.policy.models.tosca.authorative.concepts.ToscaDataType;
 
  60 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicy;
 
  61 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicyType;
 
  62 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicyTypeIdentifier;
 
  63 import org.onap.policy.models.tosca.authorative.concepts.ToscaProperty;
 
  64 import org.onap.policy.models.tosca.authorative.concepts.ToscaServiceTemplate;
 
  65 import org.onap.policy.pdp.xacml.application.common.OnapObligation;
 
  66 import org.onap.policy.pdp.xacml.application.common.PolicyApiCaller;
 
  67 import org.onap.policy.pdp.xacml.application.common.PolicyApiException;
 
  68 import org.onap.policy.pdp.xacml.application.common.ToscaDictionary;
 
  69 import org.onap.policy.pdp.xacml.application.common.ToscaPolicyConversionException;
 
  70 import org.onap.policy.pdp.xacml.application.common.ToscaPolicyTranslatorUtils;
 
  71 import org.onap.policy.pdp.xacml.application.common.XacmlApplicationException;
 
  72 import org.slf4j.Logger;
 
  73 import org.slf4j.LoggerFactory;
 
  76  * This standard matchable translator uses Policy Types that contain "matchable" field in order
 
  77  * to translate policies.
 
  79  * @author pameladragosh
 
  82 public class StdMatchableTranslator  extends StdBaseTranslator {
 
  84     private static final Logger LOGGER = LoggerFactory.getLogger(StdMatchableTranslator.class);
 
  85     private static final StandardYamlCoder standardYamlCoder = new StandardYamlCoder();
 
  87     private final Map<ToscaPolicyTypeIdentifier, ToscaServiceTemplate> matchablePolicyTypes = new HashMap<>();
 
  89     private RestServerParameters apiRestParameters;
 
  91     private Path pathForData;
 
  93     public StdMatchableTranslator() {
 
  98     public Request convertRequest(DecisionRequest request) {
 
  99         LOGGER.info("Converting Request {}", request);
 
 101             return StdMatchablePolicyRequest.createInstance(request);
 
 102         } catch (XacmlApplicationException e) {
 
 103             LOGGER.error("Failed to convert DecisionRequest: {}", e);
 
 106         // TODO throw exception
 
 112      * scanObligations - scans the list of obligations and make appropriate method calls to process
 
 115      * @param obligations Collection of obligation objects
 
 116      * @param decisionResponse DecisionResponse object used to store any results from obligations.
 
 119     protected void scanObligations(Collection<Obligation> obligations, DecisionResponse decisionResponse) {
 
 121         // Implementing a crude "closest match" on the results, which means we will strip out
 
 122         // any policies that has the lower weight than any of the others.
 
 124         // Most likely these are "default" policies with a weight of zero, but not always.
 
 126         // It is possible to have multiple policies with an equal weight, that is desired.
 
 128         // So we need to track each policy type separately and the weights for each policy.
 
 130         // policy-type -> weight -> List({policy-id, policy-content}, {policy-id, policy-content})
 
 132         Map<String, Map<Integer, List<Pair<String, Map<String, Object>>>>> closestMatches = new LinkedHashMap<>();
 
 134         // Now scan the list of obligations
 
 136         for (Obligation obligation : obligations) {
 
 137             Identifier obligationId = obligation.getId();
 
 138             LOGGER.info("Obligation: {}", obligationId);
 
 139             if (ToscaDictionary.ID_OBLIGATION_REST_BODY.equals(obligationId)) {
 
 140                 scanClosestMatchObligation(closestMatches, obligation);
 
 142                 LOGGER.warn("Unsupported Obligation Id {}", obligation.getId());
 
 146         // Now add all the policies to the DecisionResponse
 
 148         closestMatches.forEach((thePolicyType, weightMap) ->
 
 149             weightMap.forEach((weight, policies) ->
 
 150                 policies.forEach(policy -> {
 
 151                     LOGGER.info("Policy {}", policy);
 
 152                     decisionResponse.getPolicies().put(policy.getLeft(), policy.getRight());
 
 159      * scanClosestMatchObligation - scans for the obligation specifically holding policy
 
 160      * contents and their details.
 
 162      * @param closestMatches Map holding the current set of highest weight policy types
 
 163      * @param Obligation Obligation object
 
 165     protected void scanClosestMatchObligation(
 
 166             Map<String, Map<Integer, List<Pair<String, Map<String, Object>>>>> closestMatches, Obligation obligation) {
 
 168         // Create our OnapObligation object
 
 170         OnapObligation onapObligation = new OnapObligation(obligation);
 
 172         // All 4 *should* be there
 
 174         if (onapObligation.getPolicyId() == null || onapObligation.getPolicyContent() == null
 
 175                 || onapObligation.getPolicyType() == null || onapObligation.getWeight() == null) {
 
 176             LOGGER.error("Missing an expected attribute in obligation.");
 
 182         String policyId = onapObligation.getPolicyId();
 
 183         String policyType = onapObligation.getPolicyType();
 
 184         Map<String, Object> policyContent = onapObligation.getPolicyContentAsMap();
 
 185         int policyWeight = onapObligation.getWeight();
 
 187         // If the Policy Type exists, get the weight map.
 
 189         Map<Integer, List<Pair<String, Map<String, Object>>>> weightMap = closestMatches.get(policyType);
 
 190         if (weightMap != null) {
 
 192             // Only need to check first one - as we will ensure there is only one weight
 
 194             Entry<Integer, List<Pair<String, Map<String, Object>>>> firstEntry =
 
 195                     weightMap.entrySet().iterator().next();
 
 196             if (policyWeight < firstEntry.getKey()) {
 
 198                 // Existing policies have a greater weight, so we will not add it
 
 200                 LOGGER.info("{} is lesser weight {} than current policies, will not return it", policyWeight,
 
 201                         firstEntry.getKey());
 
 202             } else if (firstEntry.getKey().equals(policyWeight)) {
 
 204                 // Same weight - we will add it
 
 206                 LOGGER.info("Same weight {}, adding policy", policyWeight);
 
 207                 firstEntry.getValue().add(Pair.of(policyId, policyContent));
 
 210                 // The weight is greater, so we need to remove the other policies
 
 211                 // and point to this one.
 
 213                 LOGGER.info("New policy has greater weight {}, replacing {}", policyWeight, firstEntry.getKey());
 
 214                 List<Pair<String, Map<String, Object>>> listPolicies = new LinkedList<>();
 
 215                 listPolicies.add(Pair.of(policyId, policyContent));
 
 217                 weightMap.put(policyWeight, listPolicies);
 
 221             // Create a new entry
 
 223             LOGGER.info("New entry {} weight {}", policyType, policyWeight);
 
 224             List<Pair<String, Map<String, Object>>> listPolicies = new LinkedList<>();
 
 225             listPolicies.add(Pair.of(policyId, policyContent));
 
 226             Map<Integer, List<Pair<String, Map<String, Object>>>> newWeightMap = new LinkedHashMap<>();
 
 227             newWeightMap.put(policyWeight, listPolicies);
 
 228             closestMatches.put(policyType, newWeightMap);
 
 233     public PolicyType convertPolicy(ToscaPolicy toscaPolicy) throws ToscaPolicyConversionException {
 
 235         // Get the TOSCA Policy Type for this policy
 
 237         ToscaServiceTemplate toscaPolicyTypeTemplate = this.findPolicyType(toscaPolicy.getTypeIdentifier());
 
 239         // If we don't have any TOSCA policy types, then we cannot know
 
 240         // which properties are matchable.
 
 242         if (toscaPolicyTypeTemplate == null) {
 
 243             throw new ToscaPolicyConversionException(
 
 244                     "Cannot retrieve Policy Type definition for policy " + toscaPolicy.getName());
 
 247         // Policy name should be at the root
 
 249         String policyName = toscaPolicy.getMetadata().get(POLICY_ID);
 
 251         // Set it as the policy ID
 
 253         PolicyType newPolicyType = new PolicyType();
 
 254         newPolicyType.setPolicyId(policyName);
 
 256         // Optional description
 
 258         newPolicyType.setDescription(toscaPolicy.getDescription());
 
 260         // There should be a metadata section
 
 262         fillMetadataSection(newPolicyType, toscaPolicy.getMetadata());
 
 264         // Set the combining rule
 
 266         newPolicyType.setRuleCombiningAlgId(XACML3.ID_RULE_FIRST_APPLICABLE.stringValue());
 
 268         // Generate the TargetType - the policy should not be evaluated
 
 269         // unless all the matchable properties it cares about are matched.
 
 271         Pair<TargetType, Integer> pairGenerated = generateTargetType(toscaPolicy, toscaPolicyTypeTemplate);
 
 272         newPolicyType.setTarget(pairGenerated.getLeft());
 
 274         // Now represent the policy as Json
 
 276         StandardCoder coder = new StandardCoder();
 
 279             jsonPolicy = coder.encode(toscaPolicy);
 
 280         } catch (CoderException e) {
 
 281             throw new ToscaPolicyConversionException("Failed to encode policy to json", e);
 
 284         // Add it as an obligation
 
 286         addObligation(newPolicyType, policyName, jsonPolicy, pairGenerated.getRight(), toscaPolicy.getType());
 
 288         // Now create the Permit Rule.
 
 290         RuleType rule = new RuleType();
 
 291         rule.setDescription("Default is to PERMIT if the policy matches.");
 
 292         rule.setRuleId(policyName + ":rule");
 
 293         rule.setEffect(EffectType.PERMIT);
 
 294         rule.setTarget(new TargetType());
 
 296         // The rule contains the Condition which adds logic for
 
 297         // optional policy-type filtering.
 
 299         rule.setCondition(generateConditionForPolicyType(toscaPolicy.getType()));
 
 301         // Add the rule to the policy
 
 303         newPolicyType.getCombinerParametersOrRuleCombinerParametersOrVariableDefinition().add(rule);
 
 305         // Log output of the policy
 
 307         try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
 
 308             XACMLPolicyWriter.writePolicyFile(os, newPolicyType);
 
 309             LOGGER.info("{}", os);
 
 310         } catch (IOException e) {
 
 311             LOGGER.error("Failed to create byte array stream", e);
 
 316         return newPolicyType;
 
 320      * For generating target type, we scan for matchable properties
 
 321      * and use those to build the policy.
 
 323      * @param properties Properties section of policy
 
 324      * @param policyTypes Collection of policy Type to find matchable metadata
 
 325      * @return {@code Pair<TargetType, Integer>} Returns a TargetType and a Total Weight of matchables.
 
 327     protected Pair<TargetType, Integer> generateTargetType(ToscaPolicy policyType,
 
 328             ToscaServiceTemplate policyTemplate) {
 
 332         TargetType targetType = new TargetType();
 
 334         // Top-level list of properties
 
 336         Map<String, Object> properties = policyType.getProperties();
 
 338         // To start, we know these properties are for this specific Policy Type ID/Version
 
 340         ToscaPolicyTypeIdentifier propertiesPolicyId = policyType.getTypeIdentifier();
 
 342         // Scan the property map for matchables
 
 344         int totalWeight = findMatchablesInProperties(properties, propertiesPolicyId, policyTemplate, targetType);
 
 345         LOGGER.info("Total weight is {}", totalWeight);
 
 346         return Pair.of(targetType, totalWeight);
 
 349     protected int findMatchablesInProperties(Map<String, Object> properties,
 
 350             ToscaPolicyTypeIdentifier propertiesPolicyId,
 
 351             ToscaServiceTemplate policyTemplate,
 
 352             TargetType targetType) {
 
 353         LOGGER.info("findMatchablesInProperties from policy Type {} {}", propertiesPolicyId, properties);
 
 355         // We better have the policy type definition available from the template
 
 357         ToscaPolicyType policyType = getToscaPolicyTypeFromTemplate(propertiesPolicyId, policyTemplate);
 
 358         if (policyType == null) {
 
 359             LOGGER.error("Failed to find policy type in template {}", propertiesPolicyId);
 
 363         // Our total weight to return
 
 366         for (Entry<String, Object> entrySet : properties.entrySet()) {
 
 368             // Find the property details
 
 370             Pair<ToscaProperty, ToscaServiceTemplate> property = findProperty(entrySet.getKey(),
 
 371                     policyType, propertiesPolicyId, policyTemplate);
 
 372             if (property == null) {
 
 375             ToscaProperty toscaProperty = property.getLeft();
 
 376             LOGGER.info("Found property {} with type {} schema {}", entrySet.getKey(), toscaProperty.getType(),
 
 377                     (toscaProperty.getEntrySchema() == null ? "null" : toscaProperty.getEntrySchema().getType()));
 
 381             if (checkIsMatchableProperty(toscaProperty)) {
 
 383                 // This will generate the matchables for the property
 
 385                 int weight = generateMatchable(targetType, entrySet.getKey(), entrySet.getValue(),
 
 386                         property.getLeft(), property.getRight());
 
 387                 LOGGER.info("Weight is {}", weight);
 
 388                 totalWeight += weight;
 
 391                 // Not matchable, but we need to check if this contains list or map of datatypes.
 
 392                 // Those will need to be searched for matchables.
 
 394                 if ("list".equals(toscaProperty.getType())) {
 
 395                     int weight = findMatchablesInList(entrySet.getKey(), entrySet.getValue(), toscaProperty,
 
 396                             policyTemplate, targetType);
 
 397                     LOGGER.info("Weight list is {}", weight);
 
 398                     totalWeight += weight;
 
 399                 } else if ("map".equals(toscaProperty.getType())) {
 
 400                     int weight = findMatchablesInMap(entrySet.getKey(), entrySet.getValue(), toscaProperty,
 
 401                             policyTemplate, targetType);
 
 402                     LOGGER.info("Weight map is {}", weight);
 
 403                     totalWeight += weight;
 
 410     @SuppressWarnings("unchecked")
 
 411     protected int findMatchablesInList(String listPropertyName, Object listValue, ToscaProperty listProperty,
 
 412             ToscaServiceTemplate listTemplate, TargetType targetType) {
 
 414         // Don't bother if there is no schema (which should be a problem) or
 
 415         // its a list of primitives
 
 417         if (listProperty.getEntrySchema() == null) {
 
 418             LOGGER.error("No entry schema for list property {}", listPropertyName);
 
 422         // If they are primitives, then no need to go through them. ??
 
 424         if (isYamlType(listProperty.getEntrySchema().getType())) {
 
 425             LOGGER.info("list of primitives");
 
 431         ToscaDataType listDataType = listTemplate.getDataTypes().get(listProperty.getEntrySchema().getType());
 
 432         if (listDataType == null) {
 
 433             LOGGER.error("Unable to find datatype {}", listProperty.getEntrySchema().getType());
 
 438         for (Object datatypeValue : ((Collection<Object>)listValue)) {
 
 440             // This should be a map - because this is a list of datatypes.
 
 442             if (! (datatypeValue instanceof Map)) {
 
 443                 LOGGER.error("datatype {} value is not a map {}", listDataType.getName(), datatypeValue.getClass());
 
 446             for (Entry<String, Object> entrySet : ((Map<String, Object>)datatypeValue).entrySet()) {
 
 447                 ToscaProperty toscaProperty = listDataType.getProperties().get(entrySet.getKey());
 
 448                 if (toscaProperty == null) {
 
 449                     LOGGER.error("Failed to find datatype {} property {}", listDataType.getName(), entrySet.getKey());
 
 452                 LOGGER.info("Found list property {} with type {} schema {}", entrySet.getKey(), toscaProperty.getType(),
 
 453                         (toscaProperty.getEntrySchema() == null ? "null" : toscaProperty.getEntrySchema().getType()));
 
 457                 if (checkIsMatchableProperty(toscaProperty)) {
 
 459                     // This will generate the matchables for the property
 
 461                     int weight = generateMatchable(targetType, entrySet.getKey(), entrySet.getValue(),
 
 462                             toscaProperty, listTemplate);
 
 463                     LOGGER.info("Weight is {}", weight);
 
 464                     totalWeight += weight;
 
 467                     // Not matchable, but we need to check if this contains list or map of datatypes.
 
 468                     // Those will need to be searched for matchables.
 
 470                     if ("list".equals(toscaProperty.getType())) {
 
 471                         int weight = findMatchablesInList(entrySet.getKey(), entrySet.getValue(), toscaProperty,
 
 472                                 listTemplate, targetType);
 
 473                         LOGGER.info("Weight list is {}", weight);
 
 474                         totalWeight += weight;
 
 475                     } else if ("map".equals(toscaProperty.getType())) {
 
 476                         int weight = findMatchablesInMap(entrySet.getKey(), entrySet.getValue(), toscaProperty,
 
 477                                 listTemplate, targetType);
 
 478                         LOGGER.info("Weight map is {}", weight);
 
 479                         totalWeight += weight;
 
 488     @SuppressWarnings("unchecked")
 
 489     protected int findMatchablesInMap(String mapPropertyName, Object mapValue, ToscaProperty mapProperty,
 
 490             ToscaServiceTemplate mapTemplate, TargetType targetType) {
 
 492         // There needs to be a schema.
 
 494         if (mapProperty.getEntrySchema() == null) {
 
 495             LOGGER.error("No entry schema for map property {}", mapPropertyName);
 
 499         // If they are primitives, then no need to go through them. ??
 
 501         if (isYamlType(mapProperty.getEntrySchema().getType())) {
 
 502             LOGGER.debug("map property {} is primitives", mapPropertyName);
 
 508         ToscaDataType mapDataType = mapTemplate.getDataTypes().get(mapProperty.getEntrySchema().getType());
 
 509         if (mapDataType == null) {
 
 510             LOGGER.error("Unable to find datatype {}", mapProperty.getEntrySchema().getType());
 
 515         for (Entry<String, Object> entrySet : ((Map<String, Object>)mapValue).entrySet()) {
 
 516             ToscaProperty toscaProperty = mapDataType.getProperties().get(entrySet.getKey());
 
 517             if (toscaProperty == null) {
 
 518                 LOGGER.error("Failed to find datatype {} property {}", mapDataType.getName(), entrySet.getKey());
 
 521             LOGGER.info("Found map property {} with type {} schema {}", entrySet.getKey(), toscaProperty.getType(),
 
 522                     (toscaProperty.getEntrySchema() == null ? "null" : toscaProperty.getEntrySchema().getType()));
 
 526             if (checkIsMatchableProperty(toscaProperty)) {
 
 528                 // This will generate the matchables for the property
 
 530                 int weight = generateMatchable(targetType, entrySet.getKey(), entrySet.getValue(),
 
 531                         toscaProperty, mapTemplate);
 
 532                 LOGGER.info("Weight is {}", weight);
 
 533                 totalWeight += weight;
 
 536                 // Not matchable, but we need to check if this contains list or map of datatypes.
 
 537                 // Those will need to be searched for matchables.
 
 539                 if ("list".equals(toscaProperty.getType())) {
 
 540                     int weight = findMatchablesInList(entrySet.getKey(), entrySet.getValue(), toscaProperty,
 
 541                             mapTemplate, targetType);
 
 542                     LOGGER.info("Weight list is {}", weight);
 
 543                     totalWeight += weight;
 
 544                 } else if ("map".equals(toscaProperty.getType())) {
 
 545                     int weight = findMatchablesInMap(entrySet.getKey(), entrySet.getValue(), toscaProperty,
 
 546                             mapTemplate, targetType);
 
 547                     LOGGER.info("Weight map is {}", weight);
 
 548                     totalWeight += weight;
 
 557      * findMatchableProperty - Iterates through available TOSCA Policy Types and return the
 
 558      * ToscaProperty and template for the property.
 
 560      * @param propertyName Name of property
 
 561      * @param policyTypes Collection of TOSCA Policy Types to scan
 
 562      * @return ToscaProperty and ToscaServiceTemplate if matchable
 
 564     protected Pair<ToscaProperty, ToscaServiceTemplate> findProperty(String propertyName,
 
 565             ToscaPolicyType policyType, ToscaPolicyTypeIdentifier propertiesPolicyId,
 
 566             ToscaServiceTemplate policyTemplate) {
 
 568         // See if the property is defined by the policy template
 
 570         ToscaProperty toscaProperty = policyType.getProperties().get(propertyName);
 
 571         if (toscaProperty != null) {
 
 573             // Does it contain the matchable property and if so its set to true?
 
 575             return Pair.of(toscaProperty, policyTemplate);
 
 577         LOGGER.debug("property {} is not in policy type {}", propertyName, propertiesPolicyId);
 
 579         // Check its parent policy types
 
 581         ToscaPolicyTypeIdentifier parentId = getParentDerivedFrom(propertiesPolicyId, policyTemplate);
 
 582         while (parentId != null) {
 
 583             LOGGER.debug("searching parent policy type {}", parentId);
 
 585             // Search the existing template (should be there during runtime)
 
 587             ToscaPolicyType parentPolicyType = getParentPolicyType(parentId, policyTemplate);
 
 588             if (parentPolicyType != null) {
 
 589                 toscaProperty = parentPolicyType.getProperties().get(propertyName);
 
 590                 if (toscaProperty != null) {
 
 591                     return Pair.of(toscaProperty, policyTemplate);
 
 594                 // Move to the next parent
 
 596                 parentId = getParentDerivedFrom(parentId, policyTemplate);
 
 598                 LOGGER.warn("Parent policy type is not found {}", parentId);
 
 600                 // Find the parent policy type. During JUnit this may be in a separate
 
 601                 // file. We hope that during runtime the template is complete.
 
 603                 ToscaServiceTemplate parentTemplate = findPolicyType(parentId);
 
 604                 if (parentTemplate != null) {
 
 605                     parentPolicyType = getParentPolicyType(parentId, parentTemplate);
 
 606                     if (parentPolicyType != null) {
 
 607                         toscaProperty = parentPolicyType.getProperties().get(propertyName);
 
 608                         if (toscaProperty != null) {
 
 609                             return Pair.of(toscaProperty, parentTemplate);
 
 613                     // Move to the next parent
 
 615                     parentId = getParentDerivedFrom(parentId, parentTemplate);
 
 617                     LOGGER.error("Unable to find/pull parent policy type {}", parentId);
 
 622         LOGGER.warn("Property {} is NOT found in any template", propertyName);
 
 626     private ToscaPolicyType getToscaPolicyTypeFromTemplate(ToscaPolicyTypeIdentifier propertiesPolicyId,
 
 627             ToscaServiceTemplate policyTemplate) {
 
 628         for (Entry<String, ToscaPolicyType> entry : policyTemplate.getPolicyTypes().entrySet()) {
 
 629             if (propertiesPolicyId.getName().equals(entry.getKey())
 
 630                     && propertiesPolicyId.getVersion().equals(entry.getValue().getVersion())) {
 
 631                 return entry.getValue();
 
 637     private boolean isYamlType(String type) {
 
 638         return "string".equalsIgnoreCase(type) || "integer".equalsIgnoreCase(type) || "float".equalsIgnoreCase(type)
 
 639                 || "boolean".equalsIgnoreCase(type) || "timestamp".equalsIgnoreCase(type);
 
 643      * checkIsMatchableProperty - checks the property metadata to see if matchable exists.
 
 645      * @param toscaProperty ToscaProperty
 
 646      * @return true if matchable
 
 648     protected boolean checkIsMatchableProperty(ToscaProperty toscaProperty) {
 
 649         if (toscaProperty.getMetadata() == null) {
 
 652         for (Entry<String, String> entrySet : toscaProperty.getMetadata().entrySet()) {
 
 653             if ("matchable".equals(entrySet.getKey()) && "true".equals(entrySet.getValue())) {
 
 654                 LOGGER.debug("found matchable of type {}", toscaProperty.getType());
 
 662      * generateMatchable - Given the object, generates list of MatchType objects and add them
 
 663      * to the TargetType object. Returns a weight which is the number of AnyOf's generated. The
 
 664      * weight can be used to further filter the results for "closest match".
 
 666      * @param targetType TargetType object to add matches to
 
 667      * @param key Property key
 
 668      * @param value Object is the value - which can be a Collection or single Object
 
 669      * @param toscaProperty The property that was found
 
 670      * @param toscaServiceTemplate The template from which the property was found
 
 671      * @return int Weight of the match.
 
 673     protected int generateMatchable(TargetType targetType, String key, Object value, ToscaProperty toscaProperty,
 
 674             ToscaServiceTemplate toscaServiceTemplate) {
 
 676         if (value instanceof Collection) {
 
 678             // Further determine how we treat this collection. We will need the schema
 
 679             // if it is not available then we have to bail.
 
 681             if (toscaProperty.getEntrySchema() == null) {
 
 682                 LOGGER.error("No schema for property {} of type {}", key, toscaProperty.getType());
 
 684             if ("list".equals(toscaProperty.getType())) {
 
 685                 return generateMatchableList(targetType, key, value, toscaProperty, toscaServiceTemplate);
 
 687             if ("map".equals(toscaProperty.getType())) {
 
 688                 return generateMatchableMap(targetType, key, value, toscaProperty, toscaServiceTemplate);
 
 691             AnyOfType anyOf = generateMatches(Arrays.asList(value),
 
 692                     new IdentifierImpl(ToscaDictionary.ID_RESOURCE_MATCHABLE + key));
 
 693             if (! anyOf.getAllOf().isEmpty()) {
 
 694                 targetType.getAnyOf().add(anyOf);
 
 701     @SuppressWarnings("unchecked")
 
 702     protected int generateMatchableList(TargetType targetType, String key, Object value, ToscaProperty toscaProperty,
 
 703             ToscaServiceTemplate toscaServiceTemplate) {
 
 705         if (isYamlType(toscaProperty.getEntrySchema().getType())) {
 
 706             AnyOfType anyOf = generateMatches((Collection<Object>) value,
 
 707                     new IdentifierImpl(ToscaDictionary.ID_RESOURCE_MATCHABLE + key));
 
 708             if (! anyOf.getAllOf().isEmpty()) {
 
 709                 targetType.getAnyOf().add(anyOf);
 
 713             LOGGER.debug("PLD use datatype for list?");
 
 718     @SuppressWarnings("unchecked")
 
 719     protected int generateMatchableMap(TargetType targetType, String key, Object value, ToscaProperty toscaProperty,
 
 720             ToscaServiceTemplate toscaServiceTemplate) {
 
 722         if (isYamlType(toscaProperty.getEntrySchema().getType())) {
 
 724             // PLD TODO - this won't work
 
 726             AnyOfType anyOf = generateMatches((Collection<Object>) value,
 
 727                     new IdentifierImpl(ToscaDictionary.ID_RESOURCE_MATCHABLE + key));
 
 728             if (! anyOf.getAllOf().isEmpty()) {
 
 729                 targetType.getAnyOf().add(anyOf);
 
 733             LOGGER.debug("PLD use datatype for map?");
 
 739      * generateMatches - Goes through the collection of objects, creates a MatchType object
 
 740      * for each object and associates it with the given attribute Id. Returns the AnyOfType
 
 741      * object that contains all the generated MatchType objects.
 
 743      * @param matchables Collection of object to generate MatchType from
 
 744      * @param attributeId Given attribute Id for each MatchType
 
 745      * @return AnyOfType object
 
 747     protected AnyOfType generateMatches(Collection<Object> matchables, Identifier attributeId) {
 
 749         // This is our outer AnyOf - which is an OR
 
 751         AnyOfType anyOf = new AnyOfType();
 
 752         for (Object matchable : matchables) {
 
 756             Identifier idFunction = XACML3.ID_FUNCTION_STRING_EQUAL;
 
 757             Identifier idDatatype = XACML3.ID_DATATYPE_STRING;
 
 759             // See if we are another datatype
 
 761             // We should add datetime support. But to do that we need
 
 762             // probably more metadata to describe how that would be translated.
 
 764             if (matchable instanceof Integer) {
 
 765                 idFunction = XACML3.ID_FUNCTION_INTEGER_EQUAL;
 
 766                 idDatatype = XACML3.ID_DATATYPE_INTEGER;
 
 767             } else if (matchable instanceof Double) {
 
 768                 idFunction = XACML3.ID_FUNCTION_DOUBLE_EQUAL;
 
 769                 idDatatype = XACML3.ID_DATATYPE_DOUBLE;
 
 770             } else if (matchable instanceof Boolean) {
 
 771                 idFunction = XACML3.ID_FUNCTION_BOOLEAN_EQUAL;
 
 772                 idDatatype = XACML3.ID_DATATYPE_BOOLEAN;
 
 775             // Create a match for this
 
 777             MatchType match = ToscaPolicyTranslatorUtils.buildMatchTypeDesignator(
 
 779                     matchable.toString(),
 
 782                     XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE);
 
 784             // Now create an anyOf (OR)
 
 786             anyOf.getAllOf().add(ToscaPolicyTranslatorUtils.buildAllOf(match));
 
 791     private ToscaPolicyTypeIdentifier getParentDerivedFrom(ToscaPolicyTypeIdentifier policyTypeId,
 
 792             ToscaServiceTemplate template) {
 
 793         for (Entry<String, ToscaPolicyType> entrySet : template.getPolicyTypes().entrySet()) {
 
 794             ToscaPolicyType policyType = entrySet.getValue();
 
 795             if (entrySet.getKey().equals(policyTypeId.getName())
 
 796                     && policyType.getVersion().equals(policyTypeId.getVersion())
 
 797                     && ! "tosca.policies.Root".equals(policyType.getDerivedFrom())) {
 
 798                 return new ToscaPolicyTypeIdentifier(policyType.getDerivedFrom(), "1.0.0");
 
 805     private ToscaPolicyType getParentPolicyType(ToscaPolicyTypeIdentifier policyTypeId, ToscaServiceTemplate template) {
 
 806         for (Entry<String, ToscaPolicyType> entrySet : template.getPolicyTypes().entrySet()) {
 
 807             ToscaPolicyType policyType = entrySet.getValue();
 
 808             if (entrySet.getKey().equals(policyTypeId.getName())
 
 809                     && policyType.getVersion().equals(policyTypeId.getVersion())) {
 
 817      * findPolicyType - given the ToscaPolicyTypeIdentifier, finds it in memory, or
 
 818      * then tries to find it either locally on disk or pull it from the Policy
 
 819      * Lifecycle API the given TOSCA Policy Type.
 
 821      * @param policyTypeId ToscaPolicyTypeIdentifier to find
 
 822      * @return ToscaPolicyType object. Can be null if failure.
 
 824     private ToscaServiceTemplate findPolicyType(ToscaPolicyTypeIdentifier policyTypeId) {
 
 826         // Is it loaded in memory?
 
 828         ToscaServiceTemplate policyTemplate = this.matchablePolicyTypes.get(policyTypeId);
 
 829         if (policyTemplate == null)  {
 
 833             policyTemplate = this.loadPolicyType(policyTypeId);
 
 837             if (policyTemplate != null) {
 
 838                 this.matchablePolicyTypes.put(policyTypeId, policyTemplate);
 
 844         return policyTemplate;
 
 848      * loadPolicyType - Tries to load the given ToscaPolicyTypeIdentifier from local
 
 849      * storage. If it does not exist, will then attempt to pull from Policy Lifecycle
 
 852      * @param policyTypeId ToscaPolicyTypeIdentifier input
 
 853      * @return ToscaPolicyType object. Null if failure.
 
 855     private ToscaServiceTemplate loadPolicyType(ToscaPolicyTypeIdentifier policyTypeId) {
 
 857         // Construct what the file name should be
 
 859         Path policyTypePath = this.constructLocalFilePath(policyTypeId);
 
 866             // If it exists locally, read the bytes in
 
 868             bytes = Files.readAllBytes(policyTypePath);
 
 869         } catch (IOException e) {
 
 871             // Does not exist locally, so let's GET it from the policy api
 
 873             LOGGER.error("PolicyType not found in data area yet {}", policyTypePath, e);
 
 875             // So let's pull it from API REST call and save it locally
 
 877             return this.pullPolicyType(policyTypeId, policyTypePath);
 
 880         // Success - we have read locally the policy type. Now bring it into our
 
 883         LOGGER.info("Read in local policy type {}", policyTypePath.toAbsolutePath());
 
 885             return standardYamlCoder.decode(new String(bytes, StandardCharsets.UTF_8),
 
 886                     ToscaServiceTemplate.class);
 
 887         } catch (CoderException e) {
 
 888             LOGGER.error("Failed to decode tosca template for {}", policyTypePath, e);
 
 891         // Hopefully we never get here
 
 893         LOGGER.error("Failed to find/load policy type {}", policyTypeId);
 
 898      * pullPolicyType - pulls the given ToscaPolicyTypeIdentifier from the Policy Lifecycle API.
 
 899      * If successful, will store it locally given the policyTypePath.
 
 901      * @param policyTypeId ToscaPolicyTypeIdentifier
 
 902      * @param policyTypePath Path object to store locally
 
 903      * @return ToscaPolicyType object. Null if failure.
 
 905     private synchronized ToscaServiceTemplate pullPolicyType(ToscaPolicyTypeIdentifier policyTypeId,
 
 906             Path policyTypePath) {
 
 908         // This is what we return
 
 910         ToscaServiceTemplate policyTemplate = null;
 
 912             PolicyApiCaller api = new PolicyApiCaller(this.apiRestParameters);
 
 914             policyTemplate = api.getPolicyType(policyTypeId);
 
 915         } catch (PolicyApiException e) {
 
 916             LOGGER.error("Failed to make API call", e);
 
 917             LOGGER.error("parameters: {} ", this.apiRestParameters);
 
 920         LOGGER.info("Successfully pulled {}", policyTypeId);
 
 925             standardYamlCoder.encode(policyTypePath.toFile(), policyTemplate);
 
 926         } catch (CoderException e) {
 
 927             LOGGER.error("Failed to store {} locally to {}", policyTypeId, policyTypePath, e);
 
 930         // Done return the policy type
 
 932         return policyTemplate;
 
 936      * constructLocalFilePath - common method to ensure the name of the local file for the
 
 937      * policy type is the same.
 
 939      * @param policyTypeId ToscaPolicyTypeIdentifier
 
 940      * @return Path object
 
 942     private Path constructLocalFilePath(ToscaPolicyTypeIdentifier policyTypeId) {
 
 943         return Paths.get(this.pathForData.toAbsolutePath().toString(), policyTypeId.getName() + "-"
 
 944                 + policyTypeId.getVersion() + ".yaml");