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