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.engine;
25 import com.att.research.xacml.api.Request;
26 import com.att.research.xacml.api.Response;
27 import com.att.research.xacml.api.XACML3;
28 import com.att.research.xacml.api.pdp.PDPEngine;
29 import com.att.research.xacml.api.pdp.PDPEngineFactory;
30 import com.att.research.xacml.api.pdp.PDPException;
31 import com.att.research.xacml.util.FactoryException;
32 import com.att.research.xacml.util.XACMLPolicyScanner;
33 import com.att.research.xacml.util.XACMLProperties;
34 import com.google.common.collect.Lists;
36 import java.io.FileInputStream;
37 import java.io.FileOutputStream;
38 import java.io.IOException;
39 import java.io.InputStream;
40 import java.io.OutputStream;
41 import java.nio.file.Path;
42 import java.nio.file.Paths;
43 import java.util.ArrayList;
44 import java.util.Arrays;
45 import java.util.HashMap;
46 import java.util.List;
48 import java.util.Map.Entry;
49 import java.util.Properties;
52 import oasis.names.tc.xacml._3_0.core.schema.wd_17.AnyOfType;
53 import oasis.names.tc.xacml._3_0.core.schema.wd_17.AttributeAssignmentExpressionType;
54 import oasis.names.tc.xacml._3_0.core.schema.wd_17.AttributeValueType;
55 import oasis.names.tc.xacml._3_0.core.schema.wd_17.EffectType;
56 import oasis.names.tc.xacml._3_0.core.schema.wd_17.MatchType;
57 import oasis.names.tc.xacml._3_0.core.schema.wd_17.ObjectFactory;
58 import oasis.names.tc.xacml._3_0.core.schema.wd_17.ObligationExpressionType;
59 import oasis.names.tc.xacml._3_0.core.schema.wd_17.ObligationExpressionsType;
60 import oasis.names.tc.xacml._3_0.core.schema.wd_17.PolicySetType;
61 import oasis.names.tc.xacml._3_0.core.schema.wd_17.PolicyType;
62 import oasis.names.tc.xacml._3_0.core.schema.wd_17.RuleType;
63 import oasis.names.tc.xacml._3_0.core.schema.wd_17.TargetType;
65 import org.json.JSONObject;
66 import org.onap.policy.pdp.xacml.application.common.ToscaDictionary;
67 import org.onap.policy.pdp.xacml.application.common.ToscaPolicyConversionException;
68 import org.onap.policy.pdp.xacml.application.common.ToscaPolicyConverter;
69 import org.onap.policy.pdp.xacml.application.common.ToscaPolicyConverterUtils;
70 import org.onap.policy.pdp.xacml.application.common.XacmlApplicationServiceProvider;
71 import org.onap.policy.pdp.xacml.application.common.XacmlUpdatePolicyUtils;
72 import org.slf4j.Logger;
73 import org.slf4j.LoggerFactory;
74 import org.yaml.snakeyaml.Yaml;
77 * This is the engine class that manages the instance of the XACML PDP engine.
79 * <p>It is responsible for initializing it and shutting it down properly in a thread-safe manner.
82 * @author pameladragosh
85 public class OnapXacmlPdpEngine implements ToscaPolicyConverter, XacmlApplicationServiceProvider {
87 private static final Logger LOGGER = LoggerFactory.getLogger(OnapXacmlPdpEngine.class);
88 private static final String ONAP_MONITORING_BASE_POLICY_TYPE = "onap.Monitoring";
89 private static final String ONAP_MONITORING_DERIVED_POLICY_TYPE = "onap.policies.monitoring";
92 private Path pathForData = null;
93 private Properties pdpProperties = null;
94 private PDPEngine pdpEngine = null;
95 private Map<String, String> supportedPolicyTypes = new HashMap<>();
100 public OnapXacmlPdpEngine() {
102 // By default this supports just Monitoring policy types
104 supportedPolicyTypes.put(ONAP_MONITORING_BASE_POLICY_TYPE, "1.0.0");
108 * Load properties from given file.
110 * @param location Path and filename
111 * @throws IOException If unable to read file
113 public synchronized void loadXacmlProperties(String location) throws IOException {
114 try (InputStream is = new FileInputStream(location)) {
115 pdpProperties.load(is);
120 * Stores the XACML Properties to the given file location.
122 * @param location File location including name
123 * @throws IOException If unable to store the file.
125 public synchronized void storeXacmlProperties(String location) throws IOException {
126 try (OutputStream os = new FileOutputStream(location)) {
127 String strComments = "#";
128 pdpProperties.store(os, strComments);
133 * Make a decision call.
135 * @param request Incoming request object
136 * @return Response object
138 public synchronized Response decision(Request request) {
140 // This is what we need to return
142 Response response = null;
146 long timeStart = System.currentTimeMillis();
148 response = this.pdpEngine.decide(request);
149 } catch (PDPException e) {
150 LOGGER.error("{}", e);
153 // Track the end of timing
155 long timeEnd = System.currentTimeMillis();
156 LOGGER.info("Elapsed Time: {}ms", (timeEnd - timeStart));
162 public String applicationName() {
163 return "Monitoring Application";
167 public List<String> actionDecisionsSupported() {
168 return Arrays.asList("configure");
172 public synchronized void initialize(Path pathForData) {
176 this.pathForData = pathForData;
177 LOGGER.debug("New Path is {}", this.pathForData.toAbsolutePath());
179 // Look for and load the properties object
181 Path propertyPath = Paths.get(this.pathForData.toAbsolutePath().toString(), "xacml.properties");
182 LOGGER.debug("Looking for {}", propertyPath.toAbsolutePath());
183 try (InputStream is = new FileInputStream(propertyPath.toAbsolutePath().toString()) ) {
185 // Create a new properties object
187 pdpProperties = new Properties();
189 // Load it with our values
191 pdpProperties.load(is);
192 LOGGER.debug("{}", pdpProperties);
193 } catch (IOException e) {
194 LOGGER.error("{}", e);
197 // Now initialize the XACML PDP Engine
200 PDPEngineFactory factory = PDPEngineFactory.newInstance();
201 this.pdpEngine = factory.newEngine(pdpProperties);
202 } catch (FactoryException e) {
203 LOGGER.error("{}", e);
208 public synchronized List<String> supportedPolicyTypes() {
209 return Lists.newArrayList(supportedPolicyTypes.keySet());
213 public boolean canSupportPolicyType(String policyType, String policyTypeVersion) {
215 // For Monitoring, we will attempt to support all versions
216 // of the policy type. Since we are only packaging a decision
217 // back with a JSON payload of the property contents.
219 return (policyType.equals(ONAP_MONITORING_BASE_POLICY_TYPE)
220 || policyType.startsWith(ONAP_MONITORING_DERIVED_POLICY_TYPE));
224 public synchronized void loadPolicies(Map<String, Object> toscaPolicies) {
230 // Convert the policies first
232 List<PolicyType> listPolicies = this.convertPolicies(toscaPolicies);
233 if (listPolicies.isEmpty()) {
234 throw new ToscaPolicyConversionException("Converted 0 policies");
237 // Read in our Root Policy
239 Set<String> roots = XACMLProperties.getRootPolicyIDs(pdpProperties);
240 if (roots.isEmpty()) {
241 throw new ToscaPolicyConversionException("There are NO root policies defined");
244 // Really only should be one
246 String rootFile = pdpProperties.getProperty(roots.iterator().next() + ".file");
247 try (InputStream is = new FileInputStream(rootFile)) {
248 Object policyData = XACMLPolicyScanner.readPolicy(is);
250 // Should be a PolicySet
252 if (policyData instanceof PolicySetType) {
253 PolicyType[] newPolicies = listPolicies.toArray(new PolicyType[listPolicies.size()]);
254 PolicySetType newRootPolicy =
255 XacmlUpdatePolicyUtils.updateXacmlRootPolicy((PolicySetType) policyData, newPolicies);
257 // Save the new Policies to disk
261 // Save the root policy to disk
265 // Update properties to declare the referenced policies
269 // Write the policies to disk
273 throw new ToscaPolicyConversionException("Root policy isn't a PolicySet");
277 // Add to the root policy
279 } catch (IOException | ToscaPolicyConversionException e) {
280 LOGGER.error("Failed to loadPolicies {}", e);
285 public synchronized JSONObject makeDecision(JSONObject jsonSchema) {
290 public List<PolicyType> convertPolicies(Map<String, Object> toscaObject) throws ToscaPolicyConversionException {
292 // Return the policies
294 return scanAndConvertPolicies(toscaObject);
298 public List<PolicyType> convertPolicies(InputStream isToscaPolicy) throws ToscaPolicyConversionException {
300 // Have snakeyaml parse the object
302 Yaml yaml = new Yaml();
303 Map<String, Object> toscaObject = yaml.load(isToscaPolicy);
305 // Return the policies
307 return scanAndConvertPolicies(toscaObject);
310 @SuppressWarnings("unchecked")
311 private List<PolicyType> scanAndConvertPolicies(Map<String, Object> toscaObject)
312 throws ToscaPolicyConversionException {
316 List<PolicyType> scannedPolicies = new ArrayList<>();
318 // Iterate each of the Policies
320 List<Object> policies = (List<Object>) toscaObject.get("policies");
321 for (Object policyObject : policies) {
325 LOGGER.debug("Found policy {}", policyObject.getClass());
326 Map<String, Object> policyContents = (Map<String, Object>) policyObject;
327 for (Entry<String, Object> entrySet : policyContents.entrySet()) {
328 LOGGER.info("Entry set {}", entrySet);
330 // Convert this policy
332 PolicyType policy = this.convertPolicy(entrySet);
334 // Convert and add in the new policy
336 scannedPolicies.add(policy);
340 return scannedPolicies;
343 @SuppressWarnings("unchecked")
344 private PolicyType convertPolicy(Entry<String, Object> entrySet) throws ToscaPolicyConversionException {
346 // Policy name should be at the root
348 String policyName = entrySet.getKey();
349 Map<String, Object> policyDefinition = (Map<String, Object>) entrySet.getValue();
351 // Set it as the policy ID
353 PolicyType newPolicyType = new PolicyType();
354 newPolicyType.setPolicyId(policyName);
356 // Optional description
358 if (policyDefinition.containsKey("description")) {
359 newPolicyType.setDescription(policyDefinition.get("description").toString());
362 // There should be a metadata section
364 if (! policyDefinition.containsKey("metadata")) {
365 throw new ToscaPolicyConversionException(policyName + " missing metadata section");
367 this.fillMetadataSection(newPolicyType,
368 (Map<String, Object>) policyDefinition.get("metadata"));
370 // Set the combining rule
372 newPolicyType.setRuleCombiningAlgId(XACML3.ID_RULE_FIRST_APPLICABLE.stringValue());
374 // Generate the TargetType
377 // There should be a metadata section
379 if (! policyDefinition.containsKey("type")) {
380 throw new ToscaPolicyConversionException(policyName + " missing type value");
382 if (! policyDefinition.containsKey("version")) {
383 throw new ToscaPolicyConversionException(policyName + " missing version value");
385 TargetType target = this.generateTargetType(policyName,
386 policyDefinition.get("type").toString(),
387 policyDefinition.get("version").toString());
388 newPolicyType.setTarget(target);
390 // Now create the Permit Rule
391 // No target since the policy has a target
394 RuleType rule = new RuleType();
395 rule.setDescription("Default is to PERMIT if the policy matches.");
396 rule.setRuleId(policyName + ":rule");
397 rule.setEffect(EffectType.PERMIT);
398 rule.setTarget(new TargetType());
400 // There should be properties section - this data ends up as a
401 // JSON BLOB that is returned back to calling application.
403 if (! policyDefinition.containsKey("properties")) {
404 throw new ToscaPolicyConversionException(policyName + " missing properties section");
407 (Map<String, Object>) policyDefinition.get("properties"));
409 // Add the rule to the policy
411 newPolicyType.getCombinerParametersOrRuleCombinerParametersOrVariableDefinition().add(rule);
413 // Return our new policy
415 return newPolicyType;
419 * From the TOSCA metadata section, pull in values that are needed into the XACML policy.
421 * @param policy Policy Object to store the metadata
422 * @param metadata The Metadata TOSCA Map
423 * @return Same Policy Object
424 * @throws ToscaPolicyConversionException If there is something missing from the metadata
426 private PolicyType fillMetadataSection(PolicyType policy,
427 Map<String, Object> metadata) throws ToscaPolicyConversionException {
428 if (! metadata.containsKey("policy-id")) {
429 throw new ToscaPolicyConversionException(policy.getPolicyId() + " missing metadata policy-id");
432 // Do nothing here - the XACML PolicyId is used from TOSCA Policy Name field
435 if (! metadata.containsKey("policy-version")) {
436 throw new ToscaPolicyConversionException(policy.getPolicyId() + " missing metadata policy-version");
439 // Add in the Policy Version
441 policy.setVersion(metadata.get("policy-version").toString());
446 private TargetType generateTargetType(String policyId, String policyType, String policyTypeVersion) {
448 // Create all the match's that are possible
450 // This is for the Policy Id
452 MatchType matchPolicyId = ToscaPolicyConverterUtils.buildMatchTypeDesignator(
453 XACML3.ID_FUNCTION_STRING_EQUAL,
455 XACML3.ID_DATATYPE_STRING,
456 ToscaDictionary.ID_RESOURCE_POLICY_ID,
457 XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE);
459 // This is for the Policy Type
461 MatchType matchPolicyType = ToscaPolicyConverterUtils.buildMatchTypeDesignator(
462 XACML3.ID_FUNCTION_STRING_EQUAL,
464 XACML3.ID_DATATYPE_STRING,
465 ToscaDictionary.ID_RESOURCE_POLICY_TYPE,
466 XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE);
468 // This is for the Policy Type version
470 MatchType matchPolicyTypeVersion = ToscaPolicyConverterUtils.buildMatchTypeDesignator(
471 XACML3.ID_FUNCTION_STRING_EQUAL,
473 XACML3.ID_DATATYPE_STRING,
474 ToscaDictionary.ID_RESOURCE_POLICY_TYPE_VERSION,
475 XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE);
477 // This is our outer AnyOf - which is an OR
479 AnyOfType anyOf = new AnyOfType();
481 // Create AllOf (AND) of just Policy Id
483 anyOf.getAllOf().add(ToscaPolicyConverterUtils.buildAllOf(matchPolicyId));
485 // Create AllOf (AND) of just Policy Type
487 anyOf.getAllOf().add(ToscaPolicyConverterUtils.buildAllOf(matchPolicyType));
489 // Create AllOf (AND) of Policy Type and Policy Type Version
491 anyOf.getAllOf().add(ToscaPolicyConverterUtils.buildAllOf(matchPolicyType, matchPolicyTypeVersion));
493 // Now we can create the TargetType, add the top-level anyOf (OR),
494 // and return the value.
496 TargetType target = new TargetType();
497 target.getAnyOf().add(anyOf);
501 private RuleType addObligation(RuleType rule, Map<String, Object> properties) {
503 // Convert the YAML Policy to JSON Object
505 JSONObject jsonObject = new JSONObject(properties);
506 if (LOGGER.isDebugEnabled()) {
507 LOGGER.debug("JSON conversion {}{}", System.lineSeparator(), jsonObject);
510 // Create an AttributeValue for it
512 AttributeValueType value = new AttributeValueType();
513 value.setDataType(ToscaDictionary.ID_OBLIGATION_POLICY_MONITORING_DATATYPE.stringValue());
514 value.getContent().add(jsonObject.toString());
516 // Create our AttributeAssignmentExpression where we will
517 // store the contents of the policy in JSON format.
519 AttributeAssignmentExpressionType expressionType = new AttributeAssignmentExpressionType();
520 expressionType.setAttributeId(ToscaDictionary.ID_OBLIGATION_POLICY_MONITORING_CONTENTS.stringValue());
521 ObjectFactory factory = new ObjectFactory();
522 expressionType.setExpression(factory.createAttributeValue(value));
524 // Create an ObligationExpression for it
526 ObligationExpressionType obligation = new ObligationExpressionType();
527 obligation.setFulfillOn(EffectType.PERMIT);
528 obligation.setObligationId(ToscaDictionary.ID_OBLIGATION_REST_BODY.stringValue());
529 obligation.getAttributeAssignmentExpression().add(expressionType);
531 // Now we can add it into the rule
533 ObligationExpressionsType obligations = new ObligationExpressionsType();
534 obligations.getObligationExpression().add(obligation);
535 rule.setObligationExpressions(obligations);