2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2019 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.xacml.pdp.application.monitoring;
25 import com.att.research.xacml.api.AttributeAssignment;
26 import com.att.research.xacml.api.DataTypeException;
27 import com.att.research.xacml.api.Decision;
28 import com.att.research.xacml.api.Obligation;
29 import com.att.research.xacml.api.Request;
30 import com.att.research.xacml.api.Response;
31 import com.att.research.xacml.api.Result;
32 import com.att.research.xacml.api.XACML3;
33 import com.att.research.xacml.api.pdp.PDPEngine;
34 import com.att.research.xacml.api.pdp.PDPException;
35 import com.att.research.xacml.std.annotations.RequestParser;
36 import com.att.research.xacml.util.XACMLPolicyScanner;
37 import com.att.research.xacml.util.XACMLPolicyWriter;
38 import com.att.research.xacml.util.XACMLProperties;
39 import com.google.common.collect.Lists;
40 import com.google.gson.Gson;
42 import java.io.ByteArrayOutputStream;
43 import java.io.FileInputStream;
44 import java.io.IOException;
45 import java.io.InputStream;
46 import java.nio.file.Path;
47 import java.nio.file.Paths;
48 import java.util.ArrayList;
49 import java.util.Arrays;
50 import java.util.HashMap;
51 import java.util.List;
53 import java.util.Map.Entry;
54 import java.util.Properties;
57 import oasis.names.tc.xacml._3_0.core.schema.wd_17.AnyOfType;
58 import oasis.names.tc.xacml._3_0.core.schema.wd_17.AttributeAssignmentExpressionType;
59 import oasis.names.tc.xacml._3_0.core.schema.wd_17.AttributeValueType;
60 import oasis.names.tc.xacml._3_0.core.schema.wd_17.EffectType;
61 import oasis.names.tc.xacml._3_0.core.schema.wd_17.MatchType;
62 import oasis.names.tc.xacml._3_0.core.schema.wd_17.ObjectFactory;
63 import oasis.names.tc.xacml._3_0.core.schema.wd_17.ObligationExpressionType;
64 import oasis.names.tc.xacml._3_0.core.schema.wd_17.ObligationExpressionsType;
65 import oasis.names.tc.xacml._3_0.core.schema.wd_17.PolicySetType;
66 import oasis.names.tc.xacml._3_0.core.schema.wd_17.PolicyType;
67 import oasis.names.tc.xacml._3_0.core.schema.wd_17.RuleType;
68 import oasis.names.tc.xacml._3_0.core.schema.wd_17.TargetType;
70 import org.json.JSONObject;
71 import org.onap.policy.models.decisions.concepts.DecisionRequest;
72 import org.onap.policy.models.decisions.concepts.DecisionResponse;
73 import org.onap.policy.pdp.xacml.application.common.ToscaDictionary;
74 import org.onap.policy.pdp.xacml.application.common.ToscaPolicyConversionException;
75 import org.onap.policy.pdp.xacml.application.common.ToscaPolicyConverter;
76 import org.onap.policy.pdp.xacml.application.common.ToscaPolicyConverterUtils;
77 import org.onap.policy.pdp.xacml.application.common.XacmlApplicationServiceProvider;
78 import org.onap.policy.pdp.xacml.application.common.XacmlPolicyUtils;
79 import org.slf4j.Logger;
80 import org.slf4j.LoggerFactory;
81 import org.yaml.snakeyaml.Yaml;
84 * This is the engine class that manages the instance of the XACML PDP engine.
86 * <p>It is responsible for initializing it and shutting it down properly in a thread-safe manner.
89 * @author pameladragosh
92 public class MonitoringPdpApplication implements ToscaPolicyConverter, XacmlApplicationServiceProvider {
94 private static final Logger LOGGER = LoggerFactory.getLogger(MonitoringPdpApplication.class);
95 private static final String ONAP_MONITORING_BASE_POLICY_TYPE = "onap.Monitoring";
96 private static final String ONAP_MONITORING_DERIVED_POLICY_TYPE = "onap.policies.monitoring";
98 private Path pathForData = null;
99 private Properties pdpProperties = null;
100 private PDPEngine pdpEngine = null;
101 private Map<String, String> supportedPolicyTypes = new HashMap<>();
106 public MonitoringPdpApplication() {
108 // By default this supports just Monitoring policy types
110 supportedPolicyTypes.put(ONAP_MONITORING_BASE_POLICY_TYPE, "1.0.0");
114 public String applicationName() {
115 return "Monitoring Application";
119 public List<String> actionDecisionsSupported() {
120 return Arrays.asList("configure");
124 public synchronized void initialize(Path pathForData) {
128 this.pathForData = pathForData;
129 LOGGER.debug("New Path is {}", this.pathForData.toAbsolutePath());
131 // Look for and load the properties object
134 pdpProperties = XacmlPolicyUtils.loadXacmlProperties(XacmlPolicyUtils.getPropertiesPath(pathForData));
135 LOGGER.debug("{}", pdpProperties);
136 } catch (IOException e) {
137 LOGGER.error("{}", e);
142 PDPEngine newEngine = XacmlPolicyUtils.createEngine(pdpProperties);
143 if (newEngine != null) {
144 pdpEngine = newEngine;
149 public synchronized List<String> supportedPolicyTypes() {
150 return Lists.newArrayList(supportedPolicyTypes.keySet());
154 public boolean canSupportPolicyType(String policyType, String policyTypeVersion) {
156 // For Monitoring, we will attempt to support all versions
157 // of the policy type. Since we are only packaging a decision
158 // back with a JSON payload of the property contents.
160 return (policyType.equals(ONAP_MONITORING_BASE_POLICY_TYPE)
161 || policyType.startsWith(ONAP_MONITORING_DERIVED_POLICY_TYPE));
165 public synchronized void loadPolicies(Map<String, Object> toscaPolicies) {
168 // Convert the policies first
170 List<PolicyType> listPolicies = this.convertPolicies(toscaPolicies);
171 if (listPolicies.isEmpty()) {
172 throw new ToscaPolicyConversionException("Converted 0 policies");
175 // Read in our Root Policy
177 Set<String> roots = XACMLProperties.getRootPolicyIDs(pdpProperties);
178 if (roots.isEmpty()) {
179 throw new ToscaPolicyConversionException("There are NO root policies defined");
182 // Really only should be one
184 String rootFile = pdpProperties.getProperty(roots.iterator().next() + ".file");
185 try (InputStream is = new FileInputStream(rootFile)) {
187 // Read the Root Policy into memory
189 Object policyData = XACMLPolicyScanner.readPolicy(is);
191 // Should be a PolicySet
193 if (policyData instanceof PolicySetType) {
195 // Add the referenced policies into a new Root Policy
197 PolicyType[] newPolicies = listPolicies.toArray(new PolicyType[listPolicies.size()]);
198 PolicySetType newRootPolicy = XacmlPolicyUtils.addPoliciesToXacmlRootPolicy(
199 (PolicySetType) policyData, newPolicies);
200 LOGGER.debug("New ROOT Policy");
201 try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
202 XACMLPolicyWriter.writePolicyFile(os, newRootPolicy);
203 LOGGER.debug("{}", os);
204 } catch (IOException e) {
205 LOGGER.error("Failed to convert {}", e);
208 // Save the new Policies to disk
210 for (PolicyType policy : newPolicies) {
212 // Construct the filename
214 Path refPath = XacmlPolicyUtils.constructUniquePolicyFilename(policy, pathForData);
216 // Write the policy to disk
217 // Maybe check for an error
219 XACMLPolicyWriter.writePolicyFile(refPath, policy);
223 XacmlPolicyUtils.addReferencedPolicy(pdpProperties, refPath);
226 // Save the root policy to disk
228 XACMLPolicyWriter.writePolicyFile(Paths.get(rootFile), newRootPolicy);
230 // Write the policies to disk
232 XacmlPolicyUtils.storeXacmlProperties(pdpProperties,
233 XacmlPolicyUtils.getPropertiesPath(pathForData));
237 PDPEngine newEngine = XacmlPolicyUtils.createEngine(pdpProperties);
238 if (newEngine != null) {
239 pdpEngine = newEngine;
242 throw new ToscaPolicyConversionException("Root policy isn't a PolicySet");
245 } catch (IOException | ToscaPolicyConversionException e) {
246 LOGGER.error("Failed to loadPolicies {}", e);
251 public synchronized DecisionResponse makeDecision(DecisionRequest request) {
253 // Convert to a XacmlRequest
255 Request xacmlRequest = this.convertRequest(request);
257 // Now get a decision
259 Response xacmlResponse = this.xacmlDecision(xacmlRequest);
261 // Convert to a DecisionResponse
263 return this.convertResponse(xacmlResponse);
267 public List<PolicyType> convertPolicies(Map<String, Object> toscaObject) throws ToscaPolicyConversionException {
269 // Return the policies
271 return scanAndConvertPolicies(toscaObject);
275 public List<PolicyType> convertPolicies(InputStream isToscaPolicy) throws ToscaPolicyConversionException {
277 // Have snakeyaml parse the object
279 Yaml yaml = new Yaml();
280 Map<String, Object> toscaObject = yaml.load(isToscaPolicy);
282 // Return the policies
284 return scanAndConvertPolicies(toscaObject);
287 @SuppressWarnings("unchecked")
288 private List<PolicyType> scanAndConvertPolicies(Map<String, Object> toscaObject)
289 throws ToscaPolicyConversionException {
293 List<PolicyType> scannedPolicies = new ArrayList<>();
295 // Iterate each of the Policies
297 List<Object> policies = (List<Object>) toscaObject.get("policies");
298 for (Object policyObject : policies) {
302 LOGGER.debug("Found policy {}", policyObject.getClass());
303 Map<String, Object> policyContents = (Map<String, Object>) policyObject;
304 for (Entry<String, Object> entrySet : policyContents.entrySet()) {
305 LOGGER.debug("Entry set {}", entrySet);
307 // Convert this policy
309 PolicyType policy = this.convertPolicy(entrySet);
310 try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
311 XACMLPolicyWriter.writePolicyFile(os, policy);
312 LOGGER.debug("{}", os);
313 } catch (IOException e) {
314 LOGGER.error("Failed to convert {}", e);
317 // Convert and add in the new policy
319 scannedPolicies.add(policy);
323 return scannedPolicies;
326 @SuppressWarnings("unchecked")
327 private PolicyType convertPolicy(Entry<String, Object> entrySet) throws ToscaPolicyConversionException {
329 // Policy name should be at the root
331 String policyName = entrySet.getKey();
332 Map<String, Object> policyDefinition = (Map<String, Object>) entrySet.getValue();
334 // Set it as the policy ID
336 PolicyType newPolicyType = new PolicyType();
337 newPolicyType.setPolicyId(policyName);
339 // Optional description
341 if (policyDefinition.containsKey("description")) {
342 newPolicyType.setDescription(policyDefinition.get("description").toString());
345 // There should be a metadata section
347 if (! policyDefinition.containsKey("metadata")) {
348 throw new ToscaPolicyConversionException(policyName + " missing metadata section");
350 this.fillMetadataSection(newPolicyType,
351 (Map<String, Object>) policyDefinition.get("metadata"));
353 // Set the combining rule
355 newPolicyType.setRuleCombiningAlgId(XACML3.ID_RULE_FIRST_APPLICABLE.stringValue());
357 // Generate the TargetType
360 // There should be a metadata section
362 if (! policyDefinition.containsKey("type")) {
363 throw new ToscaPolicyConversionException(policyName + " missing type value");
365 if (! policyDefinition.containsKey("version")) {
366 throw new ToscaPolicyConversionException(policyName + " missing version value");
368 TargetType target = this.generateTargetType(policyName,
369 policyDefinition.get("type").toString(),
370 policyDefinition.get("version").toString());
371 newPolicyType.setTarget(target);
373 // Now create the Permit Rule
374 // No target since the policy has a target
377 RuleType rule = new RuleType();
378 rule.setDescription("Default is to PERMIT if the policy matches.");
379 rule.setRuleId(policyName + ":rule");
380 rule.setEffect(EffectType.PERMIT);
381 rule.setTarget(new TargetType());
383 // Now represent the policy as Json
385 JSONObject jsonObligation = new JSONObject();
386 jsonObligation.put(policyName, policyDefinition);
387 addObligation(rule, jsonObligation);
389 // Add the rule to the policy
391 newPolicyType.getCombinerParametersOrRuleCombinerParametersOrVariableDefinition().add(rule);
393 // Return our new policy
395 return newPolicyType;
399 * From the TOSCA metadata section, pull in values that are needed into the XACML policy.
401 * @param policy Policy Object to store the metadata
402 * @param metadata The Metadata TOSCA Map
403 * @return Same Policy Object
404 * @throws ToscaPolicyConversionException If there is something missing from the metadata
406 private PolicyType fillMetadataSection(PolicyType policy,
407 Map<String, Object> metadata) throws ToscaPolicyConversionException {
408 if (! metadata.containsKey("policy-id")) {
409 throw new ToscaPolicyConversionException(policy.getPolicyId() + " missing metadata policy-id");
412 // Do nothing here - the XACML PolicyId is used from TOSCA Policy Name field
415 if (! metadata.containsKey("policy-version")) {
416 throw new ToscaPolicyConversionException(policy.getPolicyId() + " missing metadata policy-version");
419 // Add in the Policy Version
421 policy.setVersion(metadata.get("policy-version").toString());
426 private TargetType generateTargetType(String policyId, String policyType, String policyTypeVersion) {
428 // Create all the match's that are possible
430 // This is for the Policy Id
432 MatchType matchPolicyId = ToscaPolicyConverterUtils.buildMatchTypeDesignator(
433 XACML3.ID_FUNCTION_STRING_EQUAL,
435 XACML3.ID_DATATYPE_STRING,
436 ToscaDictionary.ID_RESOURCE_POLICY_ID,
437 XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE);
439 // This is for the Policy Type
441 MatchType matchPolicyType = ToscaPolicyConverterUtils.buildMatchTypeDesignator(
442 XACML3.ID_FUNCTION_STRING_EQUAL,
444 XACML3.ID_DATATYPE_STRING,
445 ToscaDictionary.ID_RESOURCE_POLICY_TYPE,
446 XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE);
448 // This is for the Policy Type version
450 MatchType matchPolicyTypeVersion = ToscaPolicyConverterUtils.buildMatchTypeDesignator(
451 XACML3.ID_FUNCTION_STRING_EQUAL,
453 XACML3.ID_DATATYPE_STRING,
454 ToscaDictionary.ID_RESOURCE_POLICY_TYPE_VERSION,
455 XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE);
457 // This is our outer AnyOf - which is an OR
459 AnyOfType anyOf = new AnyOfType();
461 // Create AllOf (AND) of just Policy Id
463 anyOf.getAllOf().add(ToscaPolicyConverterUtils.buildAllOf(matchPolicyId));
465 // Create AllOf (AND) of just Policy Type
467 anyOf.getAllOf().add(ToscaPolicyConverterUtils.buildAllOf(matchPolicyType));
469 // Create AllOf (AND) of Policy Type and Policy Type Version
471 anyOf.getAllOf().add(ToscaPolicyConverterUtils.buildAllOf(matchPolicyType, matchPolicyTypeVersion));
473 // Now we can create the TargetType, add the top-level anyOf (OR),
474 // and return the value.
476 TargetType target = new TargetType();
477 target.getAnyOf().add(anyOf);
481 private RuleType addObligation(RuleType rule, JSONObject jsonPolicy) {
483 // Convert the YAML Policy to JSON Object
485 if (LOGGER.isDebugEnabled()) {
486 LOGGER.debug("JSON DCAE Policy {}{}", System.lineSeparator(), jsonPolicy);
489 // Create an AttributeValue for it
491 AttributeValueType value = new AttributeValueType();
492 value.setDataType(ToscaDictionary.ID_OBLIGATION_POLICY_MONITORING_DATATYPE.stringValue());
493 value.getContent().add(jsonPolicy.toString());
495 // Create our AttributeAssignmentExpression where we will
496 // store the contents of the policy in JSON format.
498 AttributeAssignmentExpressionType expressionType = new AttributeAssignmentExpressionType();
499 expressionType.setAttributeId(ToscaDictionary.ID_OBLIGATION_POLICY_MONITORING_CONTENTS.stringValue());
500 ObjectFactory factory = new ObjectFactory();
501 expressionType.setExpression(factory.createAttributeValue(value));
503 // Create an ObligationExpression for it
505 ObligationExpressionType obligation = new ObligationExpressionType();
506 obligation.setFulfillOn(EffectType.PERMIT);
507 obligation.setObligationId(ToscaDictionary.ID_OBLIGATION_REST_BODY.stringValue());
508 obligation.getAttributeAssignmentExpression().add(expressionType);
510 // Now we can add it into the rule
512 ObligationExpressionsType obligations = new ObligationExpressionsType();
513 obligations.getObligationExpression().add(obligation);
514 rule.setObligationExpressions(obligations);
519 public Request convertRequest(DecisionRequest request) {
520 LOGGER.debug("Converting Request {}", request);
522 return RequestParser.parseRequest(MonitoringRequest.createInstance(request));
523 } catch (IllegalArgumentException | IllegalAccessException | DataTypeException e) {
524 LOGGER.error("Failed to convert DecisionRequest: {}", e);
527 // TODO throw exception
533 public DecisionResponse convertResponse(Response xacmlResponse) {
534 LOGGER.debug("Converting Response {}", xacmlResponse);
535 DecisionResponse decisionResponse = new DecisionResponse();
537 // Iterate through all the results
539 for (Result xacmlResult : xacmlResponse.getResults()) {
543 if (xacmlResult.getDecision() == Decision.PERMIT) {
547 decisionResponse.setPolicies(new ArrayList<>());
549 // Go through obligations
551 for (Obligation obligation : xacmlResult.getObligations()) {
552 LOGGER.debug("Obligation: {}", obligation);
553 for (AttributeAssignment assignment : obligation.getAttributeAssignments()) {
554 LOGGER.debug("Attribute Assignment: {}", assignment);
556 // We care about the content attribute
558 if (ToscaDictionary.ID_OBLIGATION_POLICY_MONITORING_CONTENTS
559 .equals(assignment.getAttributeId())) {
561 // The contents are in Json form
563 Object stringContents = assignment.getAttributeValue().getValue();
564 if (LOGGER.isDebugEnabled()) {
565 LOGGER.debug("DCAE contents: {}{}", System.lineSeparator(), stringContents);
568 // Let's parse it into a map using Gson
570 Gson gson = new Gson();
571 @SuppressWarnings("unchecked")
572 Map<String, Object> result = gson.fromJson(stringContents.toString() ,Map.class);
573 decisionResponse.getPolicies().add(result);
578 decisionResponse.setErrorMessage("A better error message");
582 return decisionResponse;
586 * Make a decision call.
588 * @param request Incoming request object
589 * @return Response object
591 private synchronized Response xacmlDecision(Request request) {
593 // This is what we need to return
595 Response response = null;
599 long timeStart = System.currentTimeMillis();
601 response = this.pdpEngine.decide(request);
602 } catch (PDPException e) {
603 LOGGER.error("Xacml PDP Engine failed {}", e);
606 // Track the end of timing
608 long timeEnd = System.currentTimeMillis();
609 LOGGER.info("Elapsed Time: {}ms", (timeEnd - timeStart));