2  * ============LICENSE_START=======================================================
 
   4  * ================================================================================
 
   5  * Copyright (C) 2019-2021 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;
 
  25 import com.att.research.xacml.api.Identifier;
 
  26 import com.att.research.xacml.util.XACMLPolicyWriter;
 
  27 import com.att.research.xacml.util.XACMLProperties;
 
  29 import java.io.FileInputStream;
 
  30 import java.io.FileOutputStream;
 
  31 import java.io.IOException;
 
  32 import java.io.InputStream;
 
  33 import java.io.OutputStream;
 
  34 import java.nio.file.Files;
 
  35 import java.nio.file.Path;
 
  36 import java.nio.file.Paths;
 
  37 import java.util.Map.Entry;
 
  38 import java.util.Properties;
 
  40 import java.util.StringJoiner;
 
  41 import java.util.function.Function;
 
  42 import java.util.stream.Collectors;
 
  43 import oasis.names.tc.xacml._3_0.core.schema.wd_17.IdReferenceType;
 
  44 import oasis.names.tc.xacml._3_0.core.schema.wd_17.ObjectFactory;
 
  45 import oasis.names.tc.xacml._3_0.core.schema.wd_17.PolicySetType;
 
  46 import oasis.names.tc.xacml._3_0.core.schema.wd_17.PolicyType;
 
  47 import oasis.names.tc.xacml._3_0.core.schema.wd_17.TargetType;
 
  48 import org.slf4j.Logger;
 
  49 import org.slf4j.LoggerFactory;
 
  51 public class XacmlPolicyUtils {
 
  53     private static final Logger LOGGER = LoggerFactory.getLogger(XacmlPolicyUtils.class);
 
  55     public static final String XACML_PROPERTY_FILE = "xacml.properties";
 
  56     public static final String LINE_SEPARATOR = System.lineSeparator();
 
  58     private static final String DOT_FILE_SUFFIX = ".file";
 
  59     private static final String NOT_FOUND_MESSAGE = "NOT FOUND";
 
  62      * Function that sanitizes a file name, if the OS is Windows, so that it's a valid
 
  63      * file name. Does nothing for other OSs.
 
  65     private static final Function<String, String> SANITIZE_FILE_NAME =
 
  66                     System.getProperty("os.name").startsWith("Windows")
 
  67                     ? filename -> filename.replace(':', '_')
 
  68                     : filename -> filename;
 
  70     private XacmlPolicyUtils() {
 
  75      * Creates an empty PolicySetType object given the id and combining algorithm. Note,there
 
  76      * will also be an empty Target created. You can easily override that if need be.
 
  78      * @param policyId Policy Id
 
  79      * @param policyCombiningAlgorithm Policy Combining Algorithm
 
  80      * @return PolicySetType object
 
  82     public static PolicySetType createEmptyPolicySet(String policyId, Identifier policyCombiningAlgorithm) {
 
  83         PolicySetType policy = new PolicySetType();
 
  84         policy.setPolicySetId(policyId);
 
  85         policy.setPolicyCombiningAlgId(policyCombiningAlgorithm.stringValue());
 
  86         policy.setTarget(new TargetType());
 
  91      * Creates an empty PolicySetType object given the id and combining algorithm. Note,there
 
  92      * will also be an empty Target created. You can easily override that if need be.
 
  94      * @param policyId Policy Id
 
  95      * @param ruleCombiningAlgorithm Rule Combining Algorithm
 
  96      * @return PolicyType object
 
  98     public static PolicyType createEmptyPolicy(String policyId, Identifier ruleCombiningAlgorithm) {
 
  99         PolicyType policy = new PolicyType();
 
 100         policy.setPolicyId(policyId);
 
 101         policy.setRuleCombiningAlgId(ruleCombiningAlgorithm.stringValue());
 
 102         policy.setTarget(new TargetType());
 
 107      * This method adds a list of PolicyType objects to a root PolicySetType as
 
 108      * referenced policies.
 
 110      * @param rootPolicy Root PolicySet being updated
 
 111      * @param referencedPolicies A list of PolicyType being added as a references
 
 112      * @return the rootPolicy PolicySet object
 
 114     public static PolicySetType addPoliciesToXacmlRootPolicy(PolicySetType rootPolicy,
 
 115             PolicyType... referencedPolicies) {
 
 116         ObjectFactory factory = new ObjectFactory();
 
 118         // Iterate each policy
 
 120         for (PolicyType referencedPolicy : referencedPolicies) {
 
 121             IdReferenceType reference = new IdReferenceType();
 
 122             reference.setValue(referencedPolicy.getPolicyId());
 
 126             rootPolicy.getPolicySetOrPolicyOrPolicySetIdReference().add(factory.createPolicyIdReference(reference));
 
 129         // Return the updated object
 
 135      * This method updates a root PolicySetType by adding in a PolicyType as a reference.
 
 137      * @param rootPolicy Root PolicySet being updated
 
 138      * @param referencedPolicySets A list of PolicySetType being added as a references
 
 139      * @return the rootPolicy PolicySet object
 
 141     public static PolicySetType addPolicySetsToXacmlRootPolicy(PolicySetType rootPolicy,
 
 142             PolicySetType... referencedPolicySets) {
 
 143         ObjectFactory factory = new ObjectFactory();
 
 145         // Iterate each policy
 
 147         for (PolicySetType referencedPolicySet : referencedPolicySets) {
 
 148             IdReferenceType reference = new IdReferenceType();
 
 149             reference.setValue(referencedPolicySet.getPolicySetId());
 
 153             rootPolicy.getPolicySetOrPolicyOrPolicySetIdReference().add(factory.createPolicySetIdReference(reference));
 
 156         // Return the updated object
 
 162      * Adds in the root policy to the PDP properties object.
 
 164      * @param properties Input properties
 
 165      * @param rootPolicyPath Path to the root policy file
 
 166      * @return Properties object
 
 168     public static Properties addRootPolicy(Properties properties, Path rootPolicyPath) {
 
 170         // Get the current set of referenced policy ids
 
 172         Set<String> rootPolicies = XACMLProperties.getRootPolicyIDs(properties);
 
 174         // Construct a unique id
 
 178             String refId = "root" + id;
 
 179             if (rootPolicies.contains(refId)) {
 
 182                 rootPolicies.add(refId);
 
 183                 properties.put(refId + DOT_FILE_SUFFIX, rootPolicyPath.toAbsolutePath().toString());
 
 188         // Set the new comma separated list
 
 190         properties.setProperty(XACMLProperties.PROP_ROOTPOLICIES,
 
 191                 rootPolicies.stream().collect(Collectors.joining(",")));
 
 196      * Adds in the referenced policy to the PDP properties object.
 
 198      * @param properties Input properties
 
 199      * @param refPolicyPath Path to the referenced policy file
 
 200      * @return Properties object
 
 202     public static Properties addReferencedPolicy(Properties properties, Path refPolicyPath) {
 
 204         // Get the current set of referenced policy ids
 
 206         Set<String> referencedPolicies = XACMLProperties.getReferencedPolicyIDs(properties);
 
 208         // Construct a unique id
 
 212             String refId = "ref" + id;
 
 213             if (referencedPolicies.contains(refId)) {
 
 216                 referencedPolicies.add(refId);
 
 217                 properties.put(refId + DOT_FILE_SUFFIX, refPolicyPath.toAbsolutePath().toString());
 
 222         // Set the new comma separated list
 
 224         properties.setProperty(XACMLProperties.PROP_REFERENCEDPOLICIES,
 
 225                 referencedPolicies.stream().collect(Collectors.joining(",")));
 
 230      * Removes a root policy from the Properties object. Both in the line
 
 231      * that identifies the policy and the .file property that points to the path.
 
 233      * @param properties Input Properties object to remove
 
 234      * @param rootPolicyPath The policy file path
 
 235      * @return Properties object
 
 237     public static Properties removeRootPolicy(Properties properties, Path rootPolicyPath) {
 
 239         // Get the current set of referenced policy ids
 
 241         StringJoiner join = new StringJoiner(",");
 
 242         boolean found = false;
 
 243         Set<String> rootPolicies = XACMLProperties.getRootPolicyIDs(properties);
 
 244         for (String refPolicy : rootPolicies) {
 
 245             String refPolicyFile = refPolicy + DOT_FILE_SUFFIX;
 
 247             // If the key and value match, then it will return true
 
 249             if (properties.remove(refPolicyFile, rootPolicyPath.toString())) {
 
 251                 // Record that we actually removed it
 
 266             // Now update the list of referenced properties
 
 268             properties.setProperty(XACMLProperties.PROP_ROOTPOLICIES, join.toString());
 
 274      * Removes a referenced policy from the Properties object. Both in the line
 
 275      * that identifies the policy and the .file property that points to the path.
 
 277      * @param properties Input Properties object to remove
 
 278      * @param refPolicyPath The policy file path
 
 279      * @return Properties object
 
 281     public static Properties removeReferencedPolicy(Properties properties, Path refPolicyPath) {
 
 283         // Get the current set of referenced policy ids
 
 285         StringJoiner join = new StringJoiner(",");
 
 286         boolean found = false;
 
 287         Set<String> referencedPolicies = XACMLProperties.getReferencedPolicyIDs(properties);
 
 288         for (String refPolicy : referencedPolicies) {
 
 289             String refPolicyFile = refPolicy + DOT_FILE_SUFFIX;
 
 291             // If the key and value match, then it will return true
 
 293             if (properties.remove(refPolicyFile, refPolicyPath.toString())) {
 
 295                 // Record that we actually removed it
 
 310             // Now update the list of referenced properties
 
 312             properties.setProperty(XACMLProperties.PROP_REFERENCEDPOLICIES, join.toString());
 
 318      * Does a debug dump of referenced and root policy values.
 
 320      * @param properties Input Properties object
 
 321      * @param logger Logger object to use
 
 323     public static void debugDumpPolicyProperties(Properties properties, Logger logger) {
 
 325         // I hate surrounding this all with an if, but by
 
 326         // doing so I clear sonar issues with passing System.lineSeparator()
 
 329         if (logger.isDebugEnabled()) {
 
 331             // Get the current set of referenced policy ids
 
 333             Set<String> rootPolicies = XACMLProperties.getRootPolicyIDs(properties);
 
 334             logger.debug("Root Policies: {}", properties.getProperty(XACMLProperties.PROP_ROOTPOLICIES));
 
 335             for (String root : rootPolicies) {
 
 336                 logger.debug("{}", properties.getProperty(root + DOT_FILE_SUFFIX, NOT_FOUND_MESSAGE));
 
 339             // Get the current set of referenced policy ids
 
 341             Set<String> referencedPolicies = XACMLProperties.getReferencedPolicyIDs(properties);
 
 342             logger.debug("Referenced Policies: {}", properties.getProperty(XACMLProperties.PROP_REFERENCEDPOLICIES));
 
 343             for (String ref : referencedPolicies) {
 
 344                 logger.debug("{}", properties.getProperty(ref + DOT_FILE_SUFFIX, NOT_FOUND_MESSAGE));
 
 350      * Constructs a unique policy filename for a given policy.
 
 352      * <P>It could be dangerous to use policy-id and policy-version if the user
 
 353      * gives us an invalid policy-id and policy-versions.
 
 355      * <P>Should we append a UUID also to guarantee uniqueness?
 
 357      * <P>How do we track that in case we need to know what policies we have loaded?
 
 359      * @param policy PolicyType object
 
 360      * @param path Path for policy
 
 361      * @return Path unique file path for the Policy
 
 363     public static Path constructUniquePolicyFilename(Object policy, Path path) {
 
 366         if (policy instanceof PolicyType) {
 
 367             id = ((PolicyType) policy).getPolicyId();
 
 368             version = ((PolicyType) policy).getVersion();
 
 369         } else if (policy instanceof PolicySetType) {
 
 370             id = ((PolicySetType) policy).getPolicySetId();
 
 371             version = ((PolicySetType) policy).getVersion();
 
 373             throw new IllegalArgumentException("Must pass a PolicyType or PolicySetType");
 
 377         // Can it be possible to produce an invalid filename?
 
 378         // Should we insert a UUID
 
 380         String filename = id + "_" + version + ".xml";
 
 382         // Construct the Path
 
 384         return Paths.get(path.toAbsolutePath().toString(), SANITIZE_FILE_NAME.apply(filename));
 
 388      * Load properties from given file.
 
 390      * @throws IOException If unable to read file
 
 392     public static Properties loadXacmlProperties(Path propertyPath) throws IOException {
 
 393         LOGGER.info("Loading xacml properties {}", propertyPath);
 
 394         try (InputStream is = Files.newInputStream(propertyPath)) {
 
 395             Properties properties = new Properties();
 
 397             if (LOGGER.isInfoEnabled()) {
 
 398                 LOGGER.info("Loaded xacml properties {} {}", XacmlPolicyUtils.LINE_SEPARATOR, properties);
 
 399                 for (Entry<Object, Object> entrySet : properties.entrySet()) {
 
 400                     LOGGER.info("{} -> {}", entrySet.getKey(), entrySet.getValue());
 
 408      * Stores the XACML Properties to the given file location.
 
 410      * @throws IOException If unable to store the file.
 
 412     public static void storeXacmlProperties(Properties properties, Path propertyPath) throws IOException {
 
 413         LOGGER.info("Storing xacml properties {} {} {}", properties, XacmlPolicyUtils.LINE_SEPARATOR, propertyPath);
 
 414         try (OutputStream os = Files.newOutputStream(propertyPath)) {
 
 415             String strComments = "#";
 
 416             properties.store(os, strComments);
 
 421      * Appends 'xacml.properties' to a root Path object
 
 423      * @param rootPath Root Path object
 
 424      * @return Path to rootPath/xacml.properties file
 
 426     public static Path getPropertiesPath(Path rootPath) {
 
 427         return Paths.get(rootPath.toAbsolutePath().toString(), XACML_PROPERTY_FILE);
 
 431     public interface FileCreator {
 
 432         public File createAFile(String filename) throws IOException;
 
 437      * Copies a xacml.properties file to another location and all the policies defined within it.
 
 439      * @param propertiesPath Path to an existing properties file
 
 440      * @param properties Properties object
 
 441      * @param creator A callback that can create files. Allows JUnit test to pass Temporary folder
 
 442      * @return File object that points to new Properties file
 
 443      * @throws IOException Could not read/write files
 
 445     public static File copyXacmlPropertiesContents(String propertiesPath, Properties properties,
 
 446             FileCreator creator) throws IOException {
 
 448         // Open the properties file
 
 450         try (InputStream is = new FileInputStream(propertiesPath)) {
 
 452             // Load in the properties
 
 456             // Now we create a new xacml.properties in the temporary folder location
 
 458             File propertiesFile = creator.createAFile(XACML_PROPERTY_FILE);
 
 460             // Iterate through any root policies defined
 
 462             for (String root : XACMLProperties.getRootPolicyIDs(properties)) {
 
 466                 Path rootPath = Paths.get(properties.getProperty(root + DOT_FILE_SUFFIX));
 
 467                 LOGGER.info("Root file {} {}", rootPath, rootPath.getFileName());
 
 469                 // Construct new path for the root policy
 
 471                 File newRootPath = creator.createAFile(rootPath.getFileName().toString());
 
 473                 // Copy the policy file to the temporary folder
 
 475                 com.google.common.io.Files.copy(rootPath.toFile(), newRootPath);
 
 477                 // Change the properties object to point to where the new policy is
 
 478                 // in the temporary folder
 
 480                 properties.setProperty(root + DOT_FILE_SUFFIX, newRootPath.getAbsolutePath());
 
 483             // Iterate through any referenced policies defined
 
 485             for (String referenced : XACMLProperties.getReferencedPolicyIDs(properties)) {
 
 489                 Path refPath = Paths.get(properties.getProperty(referenced + DOT_FILE_SUFFIX));
 
 490                 LOGGER.info("Referenced file {} {}", refPath, refPath.getFileName());
 
 492                 // Construct new path for the root policy
 
 494                 File newReferencedPath = creator.createAFile(refPath.getFileName().toString());
 
 496                 // Copy the policy file to the temporary folder
 
 498                 com.google.common.io.Files.copy(refPath.toFile(), newReferencedPath);
 
 500                 // Change the properties object to point to where the new policy is
 
 501                 // in the temporary folder
 
 503                 properties.setProperty(referenced + DOT_FILE_SUFFIX, newReferencedPath.getAbsolutePath());
 
 506             // Save the new properties file to the temporary folder
 
 508             try (OutputStream os = new FileOutputStream(propertiesFile.getAbsolutePath())) {
 
 509                 properties.store(os, "");
 
 512             // Return the new path to the properties folder
 
 514             return propertiesFile;
 
 519      * Wraps the call to XACMLPolicyWriter.
 
 521      * @param path Path to file to be written to.
 
 522      * @param policy PolicyType or PolicySetType
 
 523      * @return Path - the same path passed in most likely from XACMLPolicyWriter. Or NULL if an error occurs.
 
 525     public static Path writePolicyFile(Path path, Object policy) {
 
 526         if (policy instanceof PolicyType) {
 
 527             return XACMLPolicyWriter.writePolicyFile(path, (PolicyType) policy);
 
 528         } else if (policy instanceof PolicySetType) {
 
 529             return XACMLPolicyWriter.writePolicyFile(path, (PolicySetType) policy);
 
 531             throw new IllegalArgumentException("Expecting PolicyType or PolicySetType");