Add more junit coverage to xacml-pdp (round #2)
[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 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.XACMLProperties;
27
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.stream.Collectors;
42
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
49 import org.slf4j.Logger;
50 import org.slf4j.LoggerFactory;
51
52 public class XacmlPolicyUtils {
53
54     private static final Logger LOGGER = LoggerFactory.getLogger(XacmlPolicyUtils.class);
55
56     public static final String XACML_PROPERTY_FILE = "xacml.properties";
57
58     private static final String DOT_FILE_SUFFIX = ".file";
59     private static final String NOT_FOUND_MESSAGE = "NOT FOUND";
60
61     private XacmlPolicyUtils() {
62         super();
63     }
64
65     /**
66      * Creates an empty PolicySetType object given the id and combining algorithm. Note,there
67      * will also be an empty Target created. You can easily override that if need be.
68      *
69      * @param policyId Policy Id
70      * @param policyCombiningAlgorithm Policy Combining Algorithm
71      * @return PolicySetType object
72      */
73     public static PolicySetType createEmptyPolicySet(String policyId, Identifier policyCombiningAlgorithm) {
74         PolicySetType policy = new PolicySetType();
75         policy.setPolicySetId(policyId);
76         policy.setPolicyCombiningAlgId(policyCombiningAlgorithm.stringValue());
77         policy.setTarget(new TargetType());
78         return policy;
79     }
80
81     /**
82      * Creates an empty PolicySetType object given the id and combining algorithm. Note,there
83      * will also be an empty Target created. You can easily override that if need be.
84      *
85      * @param policyId Policy Id
86      * @param ruleCombiningAlgorithm Rule Combining Algorithm
87      * @return PolicyType object
88      */
89     public static PolicyType createEmptyPolicy(String policyId, Identifier ruleCombiningAlgorithm) {
90         PolicyType policy = new PolicyType();
91         policy.setPolicyId(policyId);
92         policy.setRuleCombiningAlgId(ruleCombiningAlgorithm.stringValue());
93         policy.setTarget(new TargetType());
94         return policy;
95     }
96
97     /**
98      * This method adds a list of PolicyType objects to a root PolicySetType as
99      * referenced policies.
100      *
101      * @param rootPolicy Root PolicySet being updated
102      * @param referencedPolicies A list of PolicyType being added as a references
103      * @return the rootPolicy PolicySet object
104      */
105     public static PolicySetType addPoliciesToXacmlRootPolicy(PolicySetType rootPolicy,
106             PolicyType... referencedPolicies) {
107         ObjectFactory factory = new ObjectFactory();
108         //
109         // Iterate each policy
110         //
111         for (PolicyType referencedPolicy : referencedPolicies) {
112             IdReferenceType reference = new IdReferenceType();
113             reference.setValue(referencedPolicy.getPolicyId());
114             //
115             // Add it in
116             //
117             rootPolicy.getPolicySetOrPolicyOrPolicySetIdReference().add(factory.createPolicyIdReference(reference));
118         }
119         //
120         // Return the updated object
121         //
122         return rootPolicy;
123     }
124
125     /**
126      * This method updates a root PolicySetType by adding in a PolicyType as a reference.
127      *
128      * @param rootPolicy Root PolicySet being updated
129      * @param referencedPolicySets A list of PolicySetType being added as a references
130      * @return the rootPolicy PolicySet object
131      */
132     public static PolicySetType addPolicySetsToXacmlRootPolicy(PolicySetType rootPolicy,
133             PolicySetType... referencedPolicySets) {
134         ObjectFactory factory = new ObjectFactory();
135         //
136         // Iterate each policy
137         //
138         for (PolicySetType referencedPolicySet : referencedPolicySets) {
139             IdReferenceType reference = new IdReferenceType();
140             reference.setValue(referencedPolicySet.getPolicySetId());
141             //
142             // Add it in
143             //
144             rootPolicy.getPolicySetOrPolicyOrPolicySetIdReference().add(factory.createPolicySetIdReference(reference));
145         }
146         //
147         // Return the updated object
148         //
149         return rootPolicy;
150     }
151
152     /**
153      * Adds in the root policy to the PDP properties object.
154      *
155      * @param properties Input properties
156      * @param rootPolicyPath Path to the root policy file
157      * @return Properties object
158      */
159     public static Properties addRootPolicy(Properties properties, Path rootPolicyPath) {
160         //
161         // Get the current set of referenced policy ids
162         //
163         Set<String> rootPolicies = XACMLProperties.getRootPolicyIDs(properties);
164         //
165         // Construct a unique id
166         //
167         int id = 1;
168         while (true) {
169             String refId = "root" + id;
170             if (rootPolicies.contains(refId)) {
171                 id++;
172             } else {
173                 rootPolicies.add(refId);
174                 properties.put(refId + DOT_FILE_SUFFIX, rootPolicyPath.toAbsolutePath().toString());
175                 break;
176             }
177         }
178         //
179         // Set the new comma separated list
180         //
181         properties.setProperty(XACMLProperties.PROP_ROOTPOLICIES,
182                 rootPolicies.stream().collect(Collectors.joining(",")));
183         return properties;
184     }
185
186     /**
187      * Adds in the referenced policy to the PDP properties object.
188      *
189      * @param properties Input properties
190      * @param refPolicyPath Path to the referenced policy file
191      * @return Properties object
192      */
193     public static Properties addReferencedPolicy(Properties properties, Path refPolicyPath) {
194         //
195         // Get the current set of referenced policy ids
196         //
197         Set<String> referencedPolicies = XACMLProperties.getReferencedPolicyIDs(properties);
198         //
199         // Construct a unique id
200         //
201         int id = 1;
202         while (true) {
203             String refId = "ref" + id;
204             if (referencedPolicies.contains(refId)) {
205                 id++;
206             } else {
207                 referencedPolicies.add(refId);
208                 properties.put(refId + DOT_FILE_SUFFIX, refPolicyPath.toAbsolutePath().toString());
209                 break;
210             }
211         }
212         //
213         // Set the new comma separated list
214         //
215         properties.setProperty(XACMLProperties.PROP_REFERENCEDPOLICIES,
216                 referencedPolicies.stream().collect(Collectors.joining(",")));
217         return properties;
218     }
219
220     /**
221      * Removes a root policy from the Properties object. Both in the line
222      * that identifies the policy and the .file property that points to the path.
223      *
224      * @param properties Input Properties object to remove
225      * @param rootPolicyPath The policy file path
226      * @return Properties object
227      */
228     public static Properties removeRootPolicy(Properties properties, Path rootPolicyPath) {
229         //
230         // Get the current set of referenced policy ids
231         //
232         StringJoiner join = new StringJoiner(",");
233         boolean found = false;
234         Set<String> rootPolicies = XACMLProperties.getRootPolicyIDs(properties);
235         for (String refPolicy : rootPolicies) {
236             String refPolicyFile = refPolicy + DOT_FILE_SUFFIX;
237             //
238             // If the key and value match, then it will return true
239             //
240             if (properties.remove(refPolicyFile, rootPolicyPath.toString())) {
241                 //
242                 // Record that we actually removed it
243                 //
244                 found = true;
245             } else {
246                 //
247                 // Retain it
248                 //
249                 join.add(refPolicy);
250             }
251         }
252         //
253         // Did we remove it?
254         //
255         if (found) {
256             //
257             // Now update the list of referenced properties
258             //
259             properties.setProperty(XACMLProperties.PROP_ROOTPOLICIES, join.toString());
260         }
261         return properties;
262     }
263
264     /**
265      * Removes a referenced policy from the Properties object. Both in the line
266      * that identifies the policy and the .file property that points to the path.
267      *
268      * @param properties Input Properties object to remove
269      * @param refPolicyPath The policy file path
270      * @return Properties object
271      */
272     public static Properties removeReferencedPolicy(Properties properties, Path refPolicyPath) {
273         //
274         // Get the current set of referenced policy ids
275         //
276         StringJoiner join = new StringJoiner(",");
277         boolean found = false;
278         Set<String> referencedPolicies = XACMLProperties.getReferencedPolicyIDs(properties);
279         for (String refPolicy : referencedPolicies) {
280             String refPolicyFile = refPolicy + DOT_FILE_SUFFIX;
281             //
282             // If the key and value match, then it will return true
283             //
284             if (properties.remove(refPolicyFile, refPolicyPath.toString())) {
285                 //
286                 // Record that we actually removed it
287                 //
288                 found = true;
289             } else {
290                 //
291                 // Retain it
292                 //
293                 join.add(refPolicy);
294             }
295         }
296         //
297         // Did we remove it?
298         //
299         if (found) {
300             //
301             // Now update the list of referenced properties
302             //
303             properties.setProperty(XACMLProperties.PROP_REFERENCEDPOLICIES, join.toString());
304         }
305         return properties;
306     }
307
308     /**
309      * Does a debug dump of referenced and root policy values.
310      *
311      * @param properties Input Properties object
312      * @param logger Logger object to use
313      */
314     public static void debugDumpPolicyProperties(Properties properties, Logger logger) {
315         //
316         // I hate surrounding this all with an if, but by
317         // doing so I clear sonar issues with passing System.lineSeparator()
318         // as an argument.
319         //
320         if (logger.isDebugEnabled()) {
321             //
322             // Get the current set of referenced policy ids
323             //
324             Set<String> rootPolicies = XACMLProperties.getRootPolicyIDs(properties);
325             logger.debug("Root Policies: {}", properties.getProperty(XACMLProperties.PROP_ROOTPOLICIES));
326             for (String root : rootPolicies) {
327                 logger.debug("{}", properties.getProperty(root + DOT_FILE_SUFFIX, NOT_FOUND_MESSAGE));
328             }
329             //
330             // Get the current set of referenced policy ids
331             //
332             Set<String> referencedPolicies = XACMLProperties.getReferencedPolicyIDs(properties);
333             logger.debug("Referenced Policies: {}", properties.getProperty(XACMLProperties.PROP_REFERENCEDPOLICIES));
334             for (String ref : referencedPolicies) {
335                 logger.debug("{}", properties.getProperty(ref + DOT_FILE_SUFFIX, NOT_FOUND_MESSAGE));
336             }
337         }
338     }
339
340     /**
341      * Constructs a unique policy filename for a given policy.
342      *
343      * <P>It could be dangerous to use policy-id and policy-version if the user
344      * gives us an invalid policy-id and policy-versions.
345      *
346      * <P>Should we append a UUID also to guarantee uniqueness?
347      *
348      * <P>How do we track that in case we need to know what policies we have loaded?
349      *
350      * @param policy PolicyType object
351      * @param path Path for policy
352      * @return Path unique file path for the Policy
353      */
354     public static Path constructUniquePolicyFilename(PolicyType policy, Path path) {
355         //
356         //
357         // Can it be possible to produce an invalid filename?
358         // Should we insert a UUID
359         //
360         String filename = policy.getPolicyId() + "_" + policy.getVersion() + ".xml";
361         //
362         // Construct the Path
363         //
364         return Paths.get(path.toAbsolutePath().toString(), filename);
365     }
366
367     /**
368      * Load properties from given file.
369      *
370      * @throws IOException If unable to read file
371      */
372     public static Properties loadXacmlProperties(Path propertyPath) throws IOException {
373         LOGGER.info("Loading xacml properties {}", propertyPath);
374         try (InputStream is = Files.newInputStream(propertyPath)) {
375             Properties properties = new Properties();
376             properties.load(is);
377             if (LOGGER.isInfoEnabled()) {
378                 LOGGER.info("Loaded xacml properties {} {}", System.lineSeparator(), properties);
379                 for (Entry<Object, Object> entrySet : properties.entrySet()) {
380                     LOGGER.info("{} -> {}", entrySet.getKey(), entrySet.getValue());
381                 }
382             }
383             return properties;
384         }
385     }
386
387     /**
388      * Stores the XACML Properties to the given file location.
389      *
390      * @throws IOException If unable to store the file.
391      */
392     public static void storeXacmlProperties(Properties properties, Path propertyPath) throws IOException {
393         if (LOGGER.isInfoEnabled()) {
394             LOGGER.info("Storing xacml properties {} {} {}", properties, System.lineSeparator(), propertyPath);
395         }
396         try (OutputStream os = Files.newOutputStream(propertyPath)) {
397             String strComments = "#";
398             properties.store(os, strComments);
399         }
400     }
401
402     /**
403      * Appends 'xacml.properties' to a root Path object
404      *
405      * @param rootPath Root Path object
406      * @return Path to rootPath/xacml.properties file
407      */
408     public static Path getPropertiesPath(Path rootPath) {
409         return Paths.get(rootPath.toAbsolutePath().toString(), XACML_PROPERTY_FILE);
410     }
411
412     @FunctionalInterface
413     public interface FileCreator {
414         public File createAFile(String filename) throws IOException;
415
416     }
417
418     /**
419      * Copies a xacml.properties file to another location and all the policies defined within it.
420      *
421      * @param propertiesPath Path to an existing properties file
422      * @param properties Properties object
423      * @param creator A callback that can create files. Allows JUnit test to pass Temporary folder
424      * @return File object that points to new Properties file
425      * @throws IOException Could not read/write files
426      */
427     public static File copyXacmlPropertiesContents(String propertiesPath, Properties properties,
428             FileCreator creator) throws IOException {
429         //
430         // Open the properties file
431         //
432         try (InputStream is = new FileInputStream(propertiesPath)) {
433             //
434             // Load in the properties
435             //
436             properties.load(is);
437             //
438             // Now we create a new xacml.properties in the temporary folder location
439             //
440             File propertiesFile = creator.createAFile(XACML_PROPERTY_FILE);
441             //
442             // Iterate through any root policies defined
443             //
444             for (String root : XACMLProperties.getRootPolicyIDs(properties)) {
445                 //
446                 // Get a file
447                 //
448                 Path rootPath = Paths.get(properties.getProperty(root + DOT_FILE_SUFFIX));
449                 LOGGER.info("Root file {} {}", rootPath, rootPath.getFileName());
450                 //
451                 // Construct new path for the root policy
452                 //
453                 File newRootPath = creator.createAFile(rootPath.getFileName().toString());
454                 //
455                 // Copy the policy file to the temporary folder
456                 //
457                 com.google.common.io.Files.copy(rootPath.toFile(), newRootPath);
458                 //
459                 // Change the properties object to point to where the new policy is
460                 // in the temporary folder
461                 //
462                 properties.setProperty(root + DOT_FILE_SUFFIX, newRootPath.getAbsolutePath());
463             }
464             //
465             // Iterate through any referenced policies defined
466             //
467             for (String referenced : XACMLProperties.getReferencedPolicyIDs(properties)) {
468                 //
469                 // Get a file
470                 //
471                 Path refPath = Paths.get(properties.getProperty(referenced + DOT_FILE_SUFFIX));
472                 LOGGER.info("Referenced file {} {}", refPath, refPath.getFileName());
473                 //
474                 // Construct new path for the root policy
475                 //
476                 File newReferencedPath = creator.createAFile(refPath.getFileName().toString());
477                 //
478                 // Copy the policy file to the temporary folder
479                 //
480                 com.google.common.io.Files.copy(refPath.toFile(), newReferencedPath);
481                 //
482                 // Change the properties object to point to where the new policy is
483                 // in the temporary folder
484                 //
485                 properties.setProperty(referenced + DOT_FILE_SUFFIX, newReferencedPath.getAbsolutePath());
486             }
487             //
488             // Save the new properties file to the temporary folder
489             //
490             try (OutputStream os = new FileOutputStream(propertiesFile.getAbsolutePath())) {
491                 properties.store(os, "");
492             }
493             //
494             // Return the new path to the properties folder
495             //
496             return propertiesFile;
497         }
498     }
499 }