Fix junit for "native" on Windows
[policy/xacml-pdp.git] / applications / common / src / main / java / org / onap / policy / pdp / xacml / application / common / XacmlPolicyUtils.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * ONAP
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
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
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.
18  *
19  * SPDX-License-Identifier: Apache-2.0
20  * ============LICENSE_END=========================================================
21  */
22
23 package org.onap.policy.pdp.xacml.application.common;
24
25 import com.att.research.xacml.api.Identifier;
26 import com.att.research.xacml.util.XACMLPolicyWriter;
27 import com.att.research.xacml.util.XACMLProperties;
28 import java.io.File;
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;
39 import java.util.Set;
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;
50
51 public class XacmlPolicyUtils {
52
53     private static final Logger LOGGER = LoggerFactory.getLogger(XacmlPolicyUtils.class);
54
55     public static final String XACML_PROPERTY_FILE = "xacml.properties";
56     public static final String LINE_SEPARATOR = System.lineSeparator();
57
58     private static final String DOT_FILE_SUFFIX = ".file";
59     private static final String NOT_FOUND_MESSAGE = "NOT FOUND";
60
61     /**
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.
64      */
65     private static final Function<String, String> SANITIZE_FILE_NAME =
66                     System.getProperty("os.name").startsWith("Windows")
67                     ? filename -> filename.replace(':', '_')
68                     : filename -> filename;
69
70     private XacmlPolicyUtils() {
71         super();
72     }
73
74     /**
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.
77      *
78      * @param policyId Policy Id
79      * @param policyCombiningAlgorithm Policy Combining Algorithm
80      * @return PolicySetType object
81      */
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());
87         return policy;
88     }
89
90     /**
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.
93      *
94      * @param policyId Policy Id
95      * @param ruleCombiningAlgorithm Rule Combining Algorithm
96      * @return PolicyType object
97      */
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());
103         return policy;
104     }
105
106     /**
107      * This method adds a list of PolicyType objects to a root PolicySetType as
108      * referenced policies.
109      *
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
113      */
114     public static PolicySetType addPoliciesToXacmlRootPolicy(PolicySetType rootPolicy,
115             PolicyType... referencedPolicies) {
116         ObjectFactory factory = new ObjectFactory();
117         //
118         // Iterate each policy
119         //
120         for (PolicyType referencedPolicy : referencedPolicies) {
121             IdReferenceType reference = new IdReferenceType();
122             reference.setValue(referencedPolicy.getPolicyId());
123             //
124             // Add it in
125             //
126             rootPolicy.getPolicySetOrPolicyOrPolicySetIdReference().add(factory.createPolicyIdReference(reference));
127         }
128         //
129         // Return the updated object
130         //
131         return rootPolicy;
132     }
133
134     /**
135      * This method updates a root PolicySetType by adding in a PolicyType as a reference.
136      *
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
140      */
141     public static PolicySetType addPolicySetsToXacmlRootPolicy(PolicySetType rootPolicy,
142             PolicySetType... referencedPolicySets) {
143         ObjectFactory factory = new ObjectFactory();
144         //
145         // Iterate each policy
146         //
147         for (PolicySetType referencedPolicySet : referencedPolicySets) {
148             IdReferenceType reference = new IdReferenceType();
149             reference.setValue(referencedPolicySet.getPolicySetId());
150             //
151             // Add it in
152             //
153             rootPolicy.getPolicySetOrPolicyOrPolicySetIdReference().add(factory.createPolicySetIdReference(reference));
154         }
155         //
156         // Return the updated object
157         //
158         return rootPolicy;
159     }
160
161     /**
162      * Adds in the root policy to the PDP properties object.
163      *
164      * @param properties Input properties
165      * @param rootPolicyPath Path to the root policy file
166      * @return Properties object
167      */
168     public static Properties addRootPolicy(Properties properties, Path rootPolicyPath) {
169         //
170         // Get the current set of referenced policy ids
171         //
172         Set<String> rootPolicies = XACMLProperties.getRootPolicyIDs(properties);
173         //
174         // Construct a unique id
175         //
176         int id = 1;
177         while (true) {
178             String refId = "root" + id;
179             if (rootPolicies.contains(refId)) {
180                 id++;
181             } else {
182                 rootPolicies.add(refId);
183                 properties.put(refId + DOT_FILE_SUFFIX, rootPolicyPath.toAbsolutePath().toString());
184                 break;
185             }
186         }
187         //
188         // Set the new comma separated list
189         //
190         properties.setProperty(XACMLProperties.PROP_ROOTPOLICIES,
191                 rootPolicies.stream().collect(Collectors.joining(",")));
192         return properties;
193     }
194
195     /**
196      * Adds in the referenced policy to the PDP properties object.
197      *
198      * @param properties Input properties
199      * @param refPolicyPath Path to the referenced policy file
200      * @return Properties object
201      */
202     public static Properties addReferencedPolicy(Properties properties, Path refPolicyPath) {
203         //
204         // Get the current set of referenced policy ids
205         //
206         Set<String> referencedPolicies = XACMLProperties.getReferencedPolicyIDs(properties);
207         //
208         // Construct a unique id
209         //
210         int id = 1;
211         while (true) {
212             String refId = "ref" + id;
213             if (referencedPolicies.contains(refId)) {
214                 id++;
215             } else {
216                 referencedPolicies.add(refId);
217                 properties.put(refId + DOT_FILE_SUFFIX, refPolicyPath.toAbsolutePath().toString());
218                 break;
219             }
220         }
221         //
222         // Set the new comma separated list
223         //
224         properties.setProperty(XACMLProperties.PROP_REFERENCEDPOLICIES,
225                 referencedPolicies.stream().collect(Collectors.joining(",")));
226         return properties;
227     }
228
229     /**
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.
232      *
233      * @param properties Input Properties object to remove
234      * @param rootPolicyPath The policy file path
235      * @return Properties object
236      */
237     public static Properties removeRootPolicy(Properties properties, Path rootPolicyPath) {
238         //
239         // Get the current set of referenced policy ids
240         //
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;
246             //
247             // If the key and value match, then it will return true
248             //
249             if (properties.remove(refPolicyFile, rootPolicyPath.toString())) {
250                 //
251                 // Record that we actually removed it
252                 //
253                 found = true;
254             } else {
255                 //
256                 // Retain it
257                 //
258                 join.add(refPolicy);
259             }
260         }
261         //
262         // Did we remove it?
263         //
264         if (found) {
265             //
266             // Now update the list of referenced properties
267             //
268             properties.setProperty(XACMLProperties.PROP_ROOTPOLICIES, join.toString());
269         }
270         return properties;
271     }
272
273     /**
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.
276      *
277      * @param properties Input Properties object to remove
278      * @param refPolicyPath The policy file path
279      * @return Properties object
280      */
281     public static Properties removeReferencedPolicy(Properties properties, Path refPolicyPath) {
282         //
283         // Get the current set of referenced policy ids
284         //
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;
290             //
291             // If the key and value match, then it will return true
292             //
293             if (properties.remove(refPolicyFile, refPolicyPath.toString())) {
294                 //
295                 // Record that we actually removed it
296                 //
297                 found = true;
298             } else {
299                 //
300                 // Retain it
301                 //
302                 join.add(refPolicy);
303             }
304         }
305         //
306         // Did we remove it?
307         //
308         if (found) {
309             //
310             // Now update the list of referenced properties
311             //
312             properties.setProperty(XACMLProperties.PROP_REFERENCEDPOLICIES, join.toString());
313         }
314         return properties;
315     }
316
317     /**
318      * Does a debug dump of referenced and root policy values.
319      *
320      * @param properties Input Properties object
321      * @param logger Logger object to use
322      */
323     public static void debugDumpPolicyProperties(Properties properties, Logger logger) {
324         //
325         // I hate surrounding this all with an if, but by
326         // doing so I clear sonar issues with passing System.lineSeparator()
327         // as an argument.
328         //
329         if (logger.isDebugEnabled()) {
330             //
331             // Get the current set of referenced policy ids
332             //
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));
337             }
338             //
339             // Get the current set of referenced policy ids
340             //
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));
345             }
346         }
347     }
348
349     /**
350      * Constructs a unique policy filename for a given policy.
351      *
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.
354      *
355      * <P>Should we append a UUID also to guarantee uniqueness?
356      *
357      * <P>How do we track that in case we need to know what policies we have loaded?
358      *
359      * @param policy PolicyType object
360      * @param path Path for policy
361      * @return Path unique file path for the Policy
362      */
363     public static Path constructUniquePolicyFilename(Object policy, Path path) {
364         String id;
365         String version;
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();
372         } else {
373             throw new IllegalArgumentException("Must pass a PolicyType or PolicySetType");
374         }
375         //
376         //
377         // Can it be possible to produce an invalid filename?
378         // Should we insert a UUID
379         //
380         String filename = id + "_" + version + ".xml";
381         //
382         // Construct the Path
383         //
384         return Paths.get(path.toAbsolutePath().toString(), SANITIZE_FILE_NAME.apply(filename));
385     }
386
387     /**
388      * Load properties from given file.
389      *
390      * @throws IOException If unable to read file
391      */
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();
396             properties.load(is);
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());
401                 }
402             }
403             return properties;
404         }
405     }
406
407     /**
408      * Stores the XACML Properties to the given file location.
409      *
410      * @throws IOException If unable to store the file.
411      */
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);
417         }
418     }
419
420     /**
421      * Appends 'xacml.properties' to a root Path object
422      *
423      * @param rootPath Root Path object
424      * @return Path to rootPath/xacml.properties file
425      */
426     public static Path getPropertiesPath(Path rootPath) {
427         return Paths.get(rootPath.toAbsolutePath().toString(), XACML_PROPERTY_FILE);
428     }
429
430     @FunctionalInterface
431     public interface FileCreator {
432         public File createAFile(String filename) throws IOException;
433
434     }
435
436     /**
437      * Copies a xacml.properties file to another location and all the policies defined within it.
438      *
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
444      */
445     public static File copyXacmlPropertiesContents(String propertiesPath, Properties properties,
446             FileCreator creator) throws IOException {
447         //
448         // Open the properties file
449         //
450         try (InputStream is = new FileInputStream(propertiesPath)) {
451             //
452             // Load in the properties
453             //
454             properties.load(is);
455             //
456             // Now we create a new xacml.properties in the temporary folder location
457             //
458             File propertiesFile = creator.createAFile(XACML_PROPERTY_FILE);
459             //
460             // Iterate through any root policies defined
461             //
462             for (String root : XACMLProperties.getRootPolicyIDs(properties)) {
463                 //
464                 // Get a file
465                 //
466                 Path rootPath = Paths.get(properties.getProperty(root + DOT_FILE_SUFFIX));
467                 LOGGER.info("Root file {} {}", rootPath, rootPath.getFileName());
468                 //
469                 // Construct new path for the root policy
470                 //
471                 File newRootPath = creator.createAFile(rootPath.getFileName().toString());
472                 //
473                 // Copy the policy file to the temporary folder
474                 //
475                 com.google.common.io.Files.copy(rootPath.toFile(), newRootPath);
476                 //
477                 // Change the properties object to point to where the new policy is
478                 // in the temporary folder
479                 //
480                 properties.setProperty(root + DOT_FILE_SUFFIX, newRootPath.getAbsolutePath());
481             }
482             //
483             // Iterate through any referenced policies defined
484             //
485             for (String referenced : XACMLProperties.getReferencedPolicyIDs(properties)) {
486                 //
487                 // Get a file
488                 //
489                 Path refPath = Paths.get(properties.getProperty(referenced + DOT_FILE_SUFFIX));
490                 LOGGER.info("Referenced file {} {}", refPath, refPath.getFileName());
491                 //
492                 // Construct new path for the root policy
493                 //
494                 File newReferencedPath = creator.createAFile(refPath.getFileName().toString());
495                 //
496                 // Copy the policy file to the temporary folder
497                 //
498                 com.google.common.io.Files.copy(refPath.toFile(), newReferencedPath);
499                 //
500                 // Change the properties object to point to where the new policy is
501                 // in the temporary folder
502                 //
503                 properties.setProperty(referenced + DOT_FILE_SUFFIX, newReferencedPath.getAbsolutePath());
504             }
505             //
506             // Save the new properties file to the temporary folder
507             //
508             try (OutputStream os = new FileOutputStream(propertiesFile.getAbsolutePath())) {
509                 properties.store(os, "");
510             }
511             //
512             // Return the new path to the properties folder
513             //
514             return propertiesFile;
515         }
516     }
517
518     /**
519      * Wraps the call to XACMLPolicyWriter.
520      *
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.
524      */
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);
530         } else {
531             throw new IllegalArgumentException("Expecting PolicyType or PolicySetType");
532         }
533     }
534 }