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