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.Advice;
26 import com.att.research.xacml.api.Identifier;
27 import com.att.research.xacml.api.Obligation;
28 import com.att.research.xacml.api.Request;
29 import com.att.research.xacml.api.XACML3;
30 import com.att.research.xacml.std.IdentifierImpl;
31 import com.att.research.xacml.util.XACMLPolicyWriter;
32 import java.io.ByteArrayOutputStream;
33 import java.io.IOException;
34 import java.nio.charset.StandardCharsets;
35 import java.nio.file.Files;
36 import java.nio.file.Path;
37 import java.nio.file.Paths;
38 import java.util.Arrays;
39 import java.util.Collection;
40 import java.util.HashMap;
41 import java.util.LinkedHashMap;
42 import java.util.LinkedList;
43 import java.util.List;
45 import java.util.Map.Entry;
47 import oasis.names.tc.xacml._3_0.core.schema.wd_17.AnyOfType;
48 import oasis.names.tc.xacml._3_0.core.schema.wd_17.EffectType;
49 import oasis.names.tc.xacml._3_0.core.schema.wd_17.MatchType;
50 import oasis.names.tc.xacml._3_0.core.schema.wd_17.PolicyType;
51 import oasis.names.tc.xacml._3_0.core.schema.wd_17.RuleType;
52 import oasis.names.tc.xacml._3_0.core.schema.wd_17.TargetType;
53 import org.apache.commons.lang3.tuple.Pair;
54 import org.onap.policy.common.endpoints.parameters.RestServerParameters;
55 import org.onap.policy.common.utils.coder.CoderException;
56 import org.onap.policy.common.utils.coder.StandardCoder;
57 import org.onap.policy.common.utils.coder.StandardYamlCoder;
58 import org.onap.policy.models.decisions.concepts.DecisionRequest;
59 import org.onap.policy.models.decisions.concepts.DecisionResponse;
60 import org.onap.policy.models.tosca.authorative.concepts.ToscaDataType;
61 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicy;
62 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicyType;
63 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicyTypeIdentifier;
64 import org.onap.policy.models.tosca.authorative.concepts.ToscaProperty;
65 import org.onap.policy.models.tosca.authorative.concepts.ToscaServiceTemplate;
66 import org.onap.policy.pdp.xacml.application.common.OnapObligation;
67 import org.onap.policy.pdp.xacml.application.common.PolicyApiCaller;
68 import org.onap.policy.pdp.xacml.application.common.PolicyApiException;
69 import org.onap.policy.pdp.xacml.application.common.ToscaDictionary;
70 import org.onap.policy.pdp.xacml.application.common.ToscaPolicyConversionException;
71 import org.onap.policy.pdp.xacml.application.common.ToscaPolicyTranslatorUtils;
72 import org.onap.policy.pdp.xacml.application.common.XacmlApplicationException;
73 import org.slf4j.Logger;
74 import org.slf4j.LoggerFactory;
77 * This standard matchable translator uses Policy Types that contain "matchable" field in order
78 * to translate policies.
80 * @author pameladragosh
83 public class StdMatchableTranslator extends StdBaseTranslator {
85 private static final Logger LOGGER = LoggerFactory.getLogger(StdMatchableTranslator.class);
86 private static final StandardYamlCoder standardYamlCoder = new StandardYamlCoder();
88 private static final String MSG_WEIGHT = "Weight is {}";
89 private static final String MSG_WEIGHT_LIST = "Weight list is {}";
90 private static final String MSG_WEIGHT_MAP = "Weight map is {}";
92 private final Map<ToscaPolicyTypeIdentifier, ToscaServiceTemplate> matchablePolicyTypes = new HashMap<>();
94 private RestServerParameters apiRestParameters;
96 private Path pathForData;
98 public StdMatchableTranslator() {
103 public Request convertRequest(DecisionRequest request) throws ToscaPolicyConversionException {
104 LOGGER.info("Converting Request {}", request);
106 return StdMatchablePolicyRequest.createInstance(request);
107 } catch (XacmlApplicationException e) {
108 throw new ToscaPolicyConversionException("Failed to convert DecisionRequest", e);
113 * scanObligations - scans the list of obligations and make appropriate method calls to process
116 * @param obligations Collection of obligation objects
117 * @param decisionResponse DecisionResponse object used to store any results from obligations.
120 protected void scanObligations(Collection<Obligation> obligations, DecisionResponse decisionResponse) {
122 // Implementing a crude "closest match" on the results, which means we will strip out
123 // any policies that has the lower weight than any of the others.
125 // Most likely these are "default" policies with a weight of zero, but not always.
127 // It is possible to have multiple policies with an equal weight, that is desired.
129 // So we need to track each policy type separately and the weights for each policy.
131 // policy-type -> weight -> List({policy-id, policy-content}, {policy-id, policy-content})
133 Map<String, Map<Integer, List<Pair<String, Map<String, Object>>>>> closestMatches = new LinkedHashMap<>();
135 // Now scan the list of obligations
137 for (Obligation obligation : obligations) {
138 Identifier obligationId = obligation.getId();
139 LOGGER.info("Obligation: {}", obligationId);
140 if (ToscaDictionary.ID_OBLIGATION_REST_BODY.equals(obligationId)) {
141 scanClosestMatchObligation(closestMatches, obligation);
143 LOGGER.warn("Unsupported Obligation Id {}", obligation.getId());
147 // Now add all the policies to the DecisionResponse
149 closestMatches.forEach((thePolicyType, weightMap) ->
150 weightMap.forEach((weight, policies) ->
151 policies.forEach(policy -> {
152 LOGGER.info("Policy {}", policy);
153 decisionResponse.getPolicies().put(policy.getLeft(), policy.getRight());
159 protected void scanAdvice(Collection<Advice> advice, DecisionResponse decisionResponse) {
160 LOGGER.warn("scanAdvice not supported by {}", this.getClass());
164 * scanClosestMatchObligation - scans for the obligation specifically holding policy
165 * contents and their details.
167 * @param closestMatches Map holding the current set of highest weight policy types
168 * @param Obligation Obligation object
170 protected void scanClosestMatchObligation(
171 Map<String, Map<Integer, List<Pair<String, Map<String, Object>>>>> closestMatches, Obligation obligation) {
173 // Create our OnapObligation object
175 OnapObligation onapObligation = new OnapObligation(obligation);
177 // All 4 *should* be there
179 if (onapObligation.getPolicyId() == null || onapObligation.getPolicyContent() == null
180 || onapObligation.getPolicyType() == null || onapObligation.getWeight() == null) {
181 LOGGER.error("Missing an expected attribute in obligation.");
187 String policyId = onapObligation.getPolicyId();
188 String policyType = onapObligation.getPolicyType();
189 Map<String, Object> policyContent = onapObligation.getPolicyContentAsMap();
190 int policyWeight = onapObligation.getWeight();
192 // If the Policy Type exists, get the weight map.
194 Map<Integer, List<Pair<String, Map<String, Object>>>> weightMap = closestMatches.get(policyType);
195 if (weightMap != null) {
197 // Only need to check first one - as we will ensure there is only one weight
199 Entry<Integer, List<Pair<String, Map<String, Object>>>> firstEntry =
200 weightMap.entrySet().iterator().next();
201 if (policyWeight < firstEntry.getKey()) {
203 // Existing policies have a greater weight, so we will not add it
205 LOGGER.info("{} is lesser weight {} than current policies, will not return it", policyWeight,
206 firstEntry.getKey());
207 } else if (firstEntry.getKey().equals(policyWeight)) {
209 // Same weight - we will add it
211 LOGGER.info("Same weight {}, adding policy", policyWeight);
212 firstEntry.getValue().add(Pair.of(policyId, policyContent));
215 // The weight is greater, so we need to remove the other policies
216 // and point to this one.
218 LOGGER.info("New policy has greater weight {}, replacing {}", policyWeight, firstEntry.getKey());
219 List<Pair<String, Map<String, Object>>> listPolicies = new LinkedList<>();
220 listPolicies.add(Pair.of(policyId, policyContent));
222 weightMap.put(policyWeight, listPolicies);
226 // Create a new entry
228 LOGGER.info("New entry {} weight {}", policyType, policyWeight);
229 List<Pair<String, Map<String, Object>>> listPolicies = new LinkedList<>();
230 listPolicies.add(Pair.of(policyId, policyContent));
231 Map<Integer, List<Pair<String, Map<String, Object>>>> newWeightMap = new LinkedHashMap<>();
232 newWeightMap.put(policyWeight, listPolicies);
233 closestMatches.put(policyType, newWeightMap);
238 public PolicyType convertPolicy(ToscaPolicy toscaPolicy) throws ToscaPolicyConversionException {
240 // Get the TOSCA Policy Type for this policy
242 ToscaServiceTemplate toscaPolicyTypeTemplate = this.findPolicyType(toscaPolicy.getTypeIdentifier());
244 // If we don't have any TOSCA policy types, then we cannot know
245 // which properties are matchable.
247 if (toscaPolicyTypeTemplate == null) {
248 throw new ToscaPolicyConversionException(
249 "Cannot retrieve Policy Type definition for policy " + toscaPolicy.getName());
252 // Policy name should be at the root
254 String policyName = toscaPolicy.getMetadata().get(POLICY_ID);
256 // Set it as the policy ID
258 PolicyType newPolicyType = new PolicyType();
259 newPolicyType.setPolicyId(policyName);
261 // Optional description
263 newPolicyType.setDescription(toscaPolicy.getDescription());
265 // There should be a metadata section
267 fillMetadataSection(newPolicyType, toscaPolicy.getMetadata());
269 // Set the combining rule
271 newPolicyType.setRuleCombiningAlgId(XACML3.ID_RULE_FIRST_APPLICABLE.stringValue());
273 // Generate the TargetType - the policy should not be evaluated
274 // unless all the matchable properties it cares about are matched.
276 Pair<TargetType, Integer> pairGenerated = generateTargetType(toscaPolicy, toscaPolicyTypeTemplate);
277 newPolicyType.setTarget(pairGenerated.getLeft());
279 // Now represent the policy as Json
281 StandardCoder coder = new StandardCoder();
284 jsonPolicy = coder.encode(toscaPolicy);
285 } catch (CoderException e) {
286 throw new ToscaPolicyConversionException("Failed to encode policy to json", e);
289 // Add it as an obligation
291 addObligation(newPolicyType, policyName, jsonPolicy, pairGenerated.getRight(), toscaPolicy.getType());
293 // Now create the Permit Rule.
295 RuleType rule = new RuleType();
296 rule.setDescription("Default is to PERMIT if the policy matches.");
297 rule.setRuleId(policyName + ":rule");
298 rule.setEffect(EffectType.PERMIT);
299 rule.setTarget(new TargetType());
301 // The rule contains the Condition which adds logic for
302 // optional policy-type filtering.
304 rule.setCondition(generateConditionForPolicyType(toscaPolicy.getType()));
306 // Add the rule to the policy
308 newPolicyType.getCombinerParametersOrRuleCombinerParametersOrVariableDefinition().add(rule);
310 // Log output of the policy
312 try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
313 XACMLPolicyWriter.writePolicyFile(os, newPolicyType);
314 LOGGER.info("{}", os);
315 } catch (IOException e) {
316 LOGGER.error("Failed to create byte array stream", e);
321 return newPolicyType;
325 * For generating target type, we scan for matchable properties
326 * and use those to build the policy.
328 * @param properties Properties section of policy
329 * @param policyTypes Collection of policy Type to find matchable metadata
330 * @return {@code Pair<TargetType, Integer>} Returns a TargetType and a Total Weight of matchables.
332 protected Pair<TargetType, Integer> generateTargetType(ToscaPolicy policyType,
333 ToscaServiceTemplate policyTemplate) {
337 TargetType targetType = new TargetType();
339 // Top-level list of properties
341 Map<String, Object> properties = policyType.getProperties();
343 // To start, we know these properties are for this specific Policy Type ID/Version
345 ToscaPolicyTypeIdentifier propertiesPolicyId = policyType.getTypeIdentifier();
347 // Scan the property map for matchables
349 int totalWeight = findMatchablesInProperties(properties, propertiesPolicyId, policyTemplate, targetType);
350 LOGGER.info("Total weight is {}", totalWeight);
351 return Pair.of(targetType, totalWeight);
354 protected int findMatchablesInProperties(Map<String, Object> properties,
355 ToscaPolicyTypeIdentifier propertiesPolicyId,
356 ToscaServiceTemplate policyTemplate,
357 TargetType targetType) {
358 LOGGER.info("findMatchablesInProperties from policy Type {} {}", propertiesPolicyId, properties);
360 // We better have the policy type definition available from the template
362 ToscaPolicyType policyType = getToscaPolicyTypeFromTemplate(propertiesPolicyId, policyTemplate);
363 if (policyType == null) {
364 LOGGER.error("Failed to find policy type in template {}", propertiesPolicyId);
368 // Our total weight to return
371 for (Entry<String, Object> entrySet : properties.entrySet()) {
373 // Find the property details
375 Pair<ToscaProperty, ToscaServiceTemplate> property = findProperty(entrySet.getKey(),
376 policyType, propertiesPolicyId, policyTemplate);
377 if (property == null) {
380 ToscaProperty toscaProperty = property.getLeft();
381 LOGGER.info("Found property {} with type {} schema {}", entrySet.getKey(), toscaProperty.getType(),
382 (toscaProperty.getEntrySchema() == null ? "null" : toscaProperty.getEntrySchema().getType()));
386 if (checkIsMatchableProperty(toscaProperty)) {
388 // This will generate the matchables for the property
390 int weight = generateMatchable(targetType, entrySet.getKey(), entrySet.getValue(),
391 property.getLeft(), property.getRight());
392 LOGGER.info(MSG_WEIGHT, weight);
393 totalWeight += weight;
396 // Not matchable, but we need to check if this contains list or map of datatypes.
397 // Those will need to be searched for matchables.
399 if ("list".equals(toscaProperty.getType())) {
400 int weight = findMatchablesInList(entrySet.getKey(), entrySet.getValue(), toscaProperty,
401 policyTemplate, targetType);
402 LOGGER.info(MSG_WEIGHT_LIST, weight);
403 totalWeight += weight;
404 } else if ("map".equals(toscaProperty.getType())) {
405 int weight = findMatchablesInMap(entrySet.getKey(), entrySet.getValue(), toscaProperty,
406 policyTemplate, targetType);
407 LOGGER.info(MSG_WEIGHT_MAP, weight);
408 totalWeight += weight;
415 @SuppressWarnings("unchecked")
416 protected int findMatchablesInList(String listPropertyName, Object listValue, ToscaProperty listProperty,
417 ToscaServiceTemplate listTemplate, TargetType targetType) {
419 // Don't bother if there is no schema (which should be a problem) or
420 // its a list of primitives
422 if (listProperty.getEntrySchema() == null) {
423 LOGGER.error("No entry schema for list property {}", listPropertyName);
427 // If they are primitives, then no need to go through them. ??
429 if (isYamlType(listProperty.getEntrySchema().getType())) {
430 LOGGER.info("list of primitives");
436 ToscaDataType listDataType = listTemplate.getDataTypes().get(listProperty.getEntrySchema().getType());
437 if (listDataType == null) {
438 LOGGER.error("Unable to find datatype {}", listProperty.getEntrySchema().getType());
443 for (Object datatypeValue : ((Collection<Object>)listValue)) {
445 // This should be a map - because this is a list of datatypes.
447 if (! (datatypeValue instanceof Map)) {
448 LOGGER.error("datatype {} value is not a map {}", listDataType.getName(), datatypeValue.getClass());
451 for (Entry<String, Object> entrySet : ((Map<String, Object>)datatypeValue).entrySet()) {
452 ToscaProperty toscaProperty = listDataType.getProperties().get(entrySet.getKey());
453 if (toscaProperty == null) {
454 LOGGER.error("Failed to find datatype {} property {}", listDataType.getName(), entrySet.getKey());
457 LOGGER.info("Found list property {} with type {} schema {}", entrySet.getKey(), toscaProperty.getType(),
458 (toscaProperty.getEntrySchema() == null ? "null" : toscaProperty.getEntrySchema().getType()));
462 if (checkIsMatchableProperty(toscaProperty)) {
464 // This will generate the matchables for the property
466 int weight = generateMatchable(targetType, entrySet.getKey(), entrySet.getValue(),
467 toscaProperty, listTemplate);
468 LOGGER.info(MSG_WEIGHT, weight);
469 totalWeight += weight;
472 // Not matchable, but we need to check if this contains list or map of datatypes.
473 // Those will need to be searched for matchables.
475 if ("list".equals(toscaProperty.getType())) {
476 int weight = findMatchablesInList(entrySet.getKey(), entrySet.getValue(), toscaProperty,
477 listTemplate, targetType);
478 LOGGER.info(MSG_WEIGHT_LIST, weight);
479 totalWeight += weight;
480 } else if ("map".equals(toscaProperty.getType())) {
481 int weight = findMatchablesInMap(entrySet.getKey(), entrySet.getValue(), toscaProperty,
482 listTemplate, targetType);
483 LOGGER.info(MSG_WEIGHT_MAP, weight);
484 totalWeight += weight;
493 @SuppressWarnings("unchecked")
494 protected int findMatchablesInMap(String mapPropertyName, Object mapValue, ToscaProperty mapProperty,
495 ToscaServiceTemplate mapTemplate, TargetType targetType) {
497 // There needs to be a schema.
499 if (mapProperty.getEntrySchema() == null) {
500 LOGGER.error("No entry schema for map property {}", mapPropertyName);
504 // If they are primitives, then no need to go through them. ??
506 if (isYamlType(mapProperty.getEntrySchema().getType())) {
507 LOGGER.debug("map property {} is primitives", mapPropertyName);
513 ToscaDataType mapDataType = mapTemplate.getDataTypes().get(mapProperty.getEntrySchema().getType());
514 if (mapDataType == null) {
515 LOGGER.error("Unable to find datatype {}", mapProperty.getEntrySchema().getType());
520 for (Entry<String, Object> entrySet : ((Map<String, Object>)mapValue).entrySet()) {
521 ToscaProperty toscaProperty = mapDataType.getProperties().get(entrySet.getKey());
522 if (toscaProperty == null) {
523 LOGGER.error("Failed to find datatype {} property {}", mapDataType.getName(), entrySet.getKey());
526 LOGGER.info("Found map property {} with type {} schema {}", entrySet.getKey(), toscaProperty.getType(),
527 (toscaProperty.getEntrySchema() == null ? "null" : toscaProperty.getEntrySchema().getType()));
531 if (checkIsMatchableProperty(toscaProperty)) {
533 // This will generate the matchables for the property
535 int weight = generateMatchable(targetType, entrySet.getKey(), entrySet.getValue(),
536 toscaProperty, mapTemplate);
537 LOGGER.info(MSG_WEIGHT, weight);
538 totalWeight += weight;
541 // Not matchable, but we need to check if this contains list or map of datatypes.
542 // Those will need to be searched for matchables.
544 if ("list".equals(toscaProperty.getType())) {
545 int weight = findMatchablesInList(entrySet.getKey(), entrySet.getValue(), toscaProperty,
546 mapTemplate, targetType);
547 LOGGER.info(MSG_WEIGHT_LIST, weight);
548 totalWeight += weight;
549 } else if ("map".equals(toscaProperty.getType())) {
550 int weight = findMatchablesInMap(entrySet.getKey(), entrySet.getValue(), toscaProperty,
551 mapTemplate, targetType);
552 LOGGER.info(MSG_WEIGHT_MAP, weight);
553 totalWeight += weight;
562 * findMatchableProperty - Iterates through available TOSCA Policy Types and return the
563 * ToscaProperty and template for the property.
565 * @param propertyName Name of property
566 * @param policyTypes Collection of TOSCA Policy Types to scan
567 * @return ToscaProperty and ToscaServiceTemplate if matchable
569 protected Pair<ToscaProperty, ToscaServiceTemplate> findProperty(String propertyName,
570 ToscaPolicyType policyType, ToscaPolicyTypeIdentifier propertiesPolicyId,
571 ToscaServiceTemplate policyTemplate) {
573 // See if the property is defined by the policy template
575 ToscaProperty toscaProperty = policyType.getProperties().get(propertyName);
576 if (toscaProperty != null) {
578 // Does it contain the matchable property and if so its set to true?
580 return Pair.of(toscaProperty, policyTemplate);
582 LOGGER.debug("property {} is not in policy type {}", propertyName, propertiesPolicyId);
584 // Check its parent policy types
586 ToscaPolicyTypeIdentifier parentId = getParentDerivedFrom(propertiesPolicyId, policyTemplate);
587 while (parentId != null) {
588 LOGGER.debug("searching parent policy type {}", parentId);
590 // Search the existing template (should be there during runtime)
592 ToscaPolicyType parentPolicyType = getParentPolicyType(parentId, policyTemplate);
593 if (parentPolicyType != null) {
594 toscaProperty = parentPolicyType.getProperties().get(propertyName);
595 if (toscaProperty != null) {
596 return Pair.of(toscaProperty, policyTemplate);
599 // Move to the next parent
601 parentId = getParentDerivedFrom(parentId, policyTemplate);
603 LOGGER.warn("Parent policy type is not found {}", parentId);
605 // Find the parent policy type. During JUnit this may be in a separate
606 // file. We hope that during runtime the template is complete.
608 ToscaServiceTemplate parentTemplate = findPolicyType(parentId);
609 if (parentTemplate != null) {
610 parentPolicyType = getParentPolicyType(parentId, parentTemplate);
611 if (parentPolicyType != null) {
612 toscaProperty = parentPolicyType.getProperties().get(propertyName);
613 if (toscaProperty != null) {
614 return Pair.of(toscaProperty, parentTemplate);
618 // Move to the next parent
620 parentId = getParentDerivedFrom(parentId, parentTemplate);
622 LOGGER.error("Unable to find/pull parent policy type {}", parentId);
627 LOGGER.warn("Property {} is NOT found in any template", propertyName);
631 private ToscaPolicyType getToscaPolicyTypeFromTemplate(ToscaPolicyTypeIdentifier propertiesPolicyId,
632 ToscaServiceTemplate policyTemplate) {
633 for (Entry<String, ToscaPolicyType> entry : policyTemplate.getPolicyTypes().entrySet()) {
634 if (propertiesPolicyId.getName().equals(entry.getKey())
635 && propertiesPolicyId.getVersion().equals(entry.getValue().getVersion())) {
636 return entry.getValue();
642 private boolean isYamlType(String type) {
643 return "string".equalsIgnoreCase(type) || "integer".equalsIgnoreCase(type) || "float".equalsIgnoreCase(type)
644 || "boolean".equalsIgnoreCase(type) || "timestamp".equalsIgnoreCase(type);
648 * checkIsMatchableProperty - checks the property metadata to see if matchable exists.
650 * @param toscaProperty ToscaProperty
651 * @return true if matchable
653 protected boolean checkIsMatchableProperty(ToscaProperty toscaProperty) {
654 if (toscaProperty.getMetadata() == null) {
657 for (Entry<String, String> entrySet : toscaProperty.getMetadata().entrySet()) {
658 if ("matchable".equals(entrySet.getKey()) && "true".equals(entrySet.getValue())) {
659 LOGGER.debug("found matchable of type {}", toscaProperty.getType());
667 * generateMatchable - Given the object, generates list of MatchType objects and add them
668 * to the TargetType object. Returns a weight which is the number of AnyOf's generated. The
669 * weight can be used to further filter the results for "closest match".
671 * @param targetType TargetType object to add matches to
672 * @param key Property key
673 * @param value Object is the value - which can be a Collection or single Object
674 * @param toscaProperty The property that was found
675 * @param toscaServiceTemplate The template from which the property was found
676 * @return int Weight of the match.
678 protected int generateMatchable(TargetType targetType, String key, Object value, ToscaProperty toscaProperty,
679 ToscaServiceTemplate toscaServiceTemplate) {
681 if (value instanceof Collection) {
683 // Further determine how we treat this collection. We will need the schema
684 // if it is not available then we have to bail.
686 if (toscaProperty.getEntrySchema() == null) {
687 LOGGER.error("No schema for property {} of type {}", key, toscaProperty.getType());
689 if ("list".equals(toscaProperty.getType())) {
690 return generateMatchableList(targetType, key, value, toscaProperty, toscaServiceTemplate);
692 if ("map".equals(toscaProperty.getType())) {
693 return generateMatchableMap(targetType, key, value, toscaProperty, toscaServiceTemplate);
696 AnyOfType anyOf = generateMatches(Arrays.asList(value),
697 new IdentifierImpl(ToscaDictionary.ID_RESOURCE_MATCHABLE + key));
698 if (! anyOf.getAllOf().isEmpty()) {
699 targetType.getAnyOf().add(anyOf);
706 @SuppressWarnings("unchecked")
707 protected int generateMatchableList(TargetType targetType, String key, Object value, ToscaProperty toscaProperty,
708 ToscaServiceTemplate toscaServiceTemplate) {
710 if (isYamlType(toscaProperty.getEntrySchema().getType())) {
711 AnyOfType anyOf = generateMatches((Collection<Object>) value,
712 new IdentifierImpl(ToscaDictionary.ID_RESOURCE_MATCHABLE + key));
713 if (! anyOf.getAllOf().isEmpty()) {
714 targetType.getAnyOf().add(anyOf);
718 LOGGER.debug("PLD use datatype for list?");
723 @SuppressWarnings("unchecked")
724 protected int generateMatchableMap(TargetType targetType, String key, Object value, ToscaProperty toscaProperty,
725 ToscaServiceTemplate toscaServiceTemplate) {
727 if (isYamlType(toscaProperty.getEntrySchema().getType())) {
729 // PLD TODO - this won't work. Right now there are no maps being used to match.
730 // need to investigate whether we really can support that situation.
732 AnyOfType anyOf = generateMatches((Collection<Object>) value,
733 new IdentifierImpl(ToscaDictionary.ID_RESOURCE_MATCHABLE + key));
734 if (! anyOf.getAllOf().isEmpty()) {
735 targetType.getAnyOf().add(anyOf);
739 LOGGER.debug("PLD use datatype for map?");
745 * generateMatches - Goes through the collection of objects, creates a MatchType object
746 * for each object and associates it with the given attribute Id. Returns the AnyOfType
747 * object that contains all the generated MatchType objects.
749 * @param matchables Collection of object to generate MatchType from
750 * @param attributeId Given attribute Id for each MatchType
751 * @return AnyOfType object
753 protected AnyOfType generateMatches(Collection<Object> matchables, Identifier attributeId) {
755 // This is our outer AnyOf - which is an OR
757 AnyOfType anyOf = new AnyOfType();
758 for (Object matchable : matchables) {
762 Identifier idFunction = XACML3.ID_FUNCTION_STRING_EQUAL;
763 Identifier idDatatype = XACML3.ID_DATATYPE_STRING;
765 // See if we are another datatype
767 // We should add datetime support. But to do that we need
768 // probably more metadata to describe how that would be translated.
770 if (matchable instanceof Integer) {
771 idFunction = XACML3.ID_FUNCTION_INTEGER_EQUAL;
772 idDatatype = XACML3.ID_DATATYPE_INTEGER;
773 } else if (matchable instanceof Double) {
774 idFunction = XACML3.ID_FUNCTION_DOUBLE_EQUAL;
775 idDatatype = XACML3.ID_DATATYPE_DOUBLE;
776 } else if (matchable instanceof Boolean) {
777 idFunction = XACML3.ID_FUNCTION_BOOLEAN_EQUAL;
778 idDatatype = XACML3.ID_DATATYPE_BOOLEAN;
781 // Create a match for this
783 MatchType match = ToscaPolicyTranslatorUtils.buildMatchTypeDesignator(
785 matchable.toString(),
788 XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE);
790 // Now create an anyOf (OR)
792 anyOf.getAllOf().add(ToscaPolicyTranslatorUtils.buildAllOf(match));
797 private ToscaPolicyTypeIdentifier getParentDerivedFrom(ToscaPolicyTypeIdentifier policyTypeId,
798 ToscaServiceTemplate template) {
799 for (Entry<String, ToscaPolicyType> entrySet : template.getPolicyTypes().entrySet()) {
800 ToscaPolicyType policyType = entrySet.getValue();
801 if (entrySet.getKey().equals(policyTypeId.getName())
802 && policyType.getVersion().equals(policyTypeId.getVersion())
803 && ! "tosca.policies.Root".equals(policyType.getDerivedFrom())) {
804 return new ToscaPolicyTypeIdentifier(policyType.getDerivedFrom(), "1.0.0");
811 private ToscaPolicyType getParentPolicyType(ToscaPolicyTypeIdentifier policyTypeId, ToscaServiceTemplate template) {
812 for (Entry<String, ToscaPolicyType> entrySet : template.getPolicyTypes().entrySet()) {
813 ToscaPolicyType policyType = entrySet.getValue();
814 if (entrySet.getKey().equals(policyTypeId.getName())
815 && policyType.getVersion().equals(policyTypeId.getVersion())) {
823 * findPolicyType - given the ToscaPolicyTypeIdentifier, finds it in memory, or
824 * then tries to find it either locally on disk or pull it from the Policy
825 * Lifecycle API the given TOSCA Policy Type.
827 * @param policyTypeId ToscaPolicyTypeIdentifier to find
828 * @return ToscaPolicyType object. Can be null if failure.
830 private ToscaServiceTemplate findPolicyType(ToscaPolicyTypeIdentifier policyTypeId) {
832 // Is it loaded in memory?
834 ToscaServiceTemplate policyTemplate = this.matchablePolicyTypes.get(policyTypeId);
835 if (policyTemplate == null) {
839 policyTemplate = this.loadPolicyType(policyTypeId);
843 if (policyTemplate != null) {
844 this.matchablePolicyTypes.put(policyTypeId, policyTemplate);
850 return policyTemplate;
854 * loadPolicyType - Tries to load the given ToscaPolicyTypeIdentifier from local
855 * storage. If it does not exist, will then attempt to pull from Policy Lifecycle
858 * @param policyTypeId ToscaPolicyTypeIdentifier input
859 * @return ToscaPolicyType object. Null if failure.
861 private ToscaServiceTemplate loadPolicyType(ToscaPolicyTypeIdentifier policyTypeId) {
863 // Construct what the file name should be
865 Path policyTypePath = this.constructLocalFilePath(policyTypeId);
872 // If it exists locally, read the bytes in
874 bytes = Files.readAllBytes(policyTypePath);
875 } catch (IOException e) {
877 // Does not exist locally, so let's GET it from the policy api
879 LOGGER.error("PolicyType not found in data area yet {}", policyTypePath, e);
881 // So let's pull it from API REST call and save it locally
883 return this.pullPolicyType(policyTypeId, policyTypePath);
886 // Success - we have read locally the policy type. Now bring it into our
889 LOGGER.info("Read in local policy type {}", policyTypePath.toAbsolutePath());
891 return standardYamlCoder.decode(new String(bytes, StandardCharsets.UTF_8),
892 ToscaServiceTemplate.class);
893 } catch (CoderException e) {
894 LOGGER.error("Failed to decode tosca template for {}", policyTypePath, e);
897 // Hopefully we never get here
899 LOGGER.error("Failed to find/load policy type {}", policyTypeId);
904 * pullPolicyType - pulls the given ToscaPolicyTypeIdentifier from the Policy Lifecycle API.
905 * If successful, will store it locally given the policyTypePath.
907 * @param policyTypeId ToscaPolicyTypeIdentifier
908 * @param policyTypePath Path object to store locally
909 * @return ToscaPolicyType object. Null if failure.
911 private synchronized ToscaServiceTemplate pullPolicyType(ToscaPolicyTypeIdentifier policyTypeId,
912 Path policyTypePath) {
914 // This is what we return
916 ToscaServiceTemplate policyTemplate = null;
918 PolicyApiCaller api = new PolicyApiCaller(this.apiRestParameters);
920 policyTemplate = api.getPolicyType(policyTypeId);
921 } catch (PolicyApiException e) {
922 LOGGER.error("Failed to make API call", e);
923 LOGGER.error("parameters: {} ", this.apiRestParameters);
926 LOGGER.info("Successfully pulled {}", policyTypeId);
931 standardYamlCoder.encode(policyTypePath.toFile(), policyTemplate);
932 } catch (CoderException e) {
933 LOGGER.error("Failed to store {} locally to {}", policyTypeId, policyTypePath, e);
936 // Done return the policy type
938 return policyTemplate;
942 * constructLocalFilePath - common method to ensure the name of the local file for the
943 * policy type is the same.
945 * @param policyTypeId ToscaPolicyTypeIdentifier
946 * @return Path object
948 private Path constructLocalFilePath(ToscaPolicyTypeIdentifier policyTypeId) {
949 return Paths.get(this.pathForData.toAbsolutePath().toString(), policyTypeId.getName() + "-"
950 + policyTypeId.getVersion() + ".yaml");