364b651913c83f882d29da79dfe6d95902a0bf77
[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 referenced 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 refPolicyPath The policy file path
223      * @return Properties object
224      */
225     public static Properties removeReferencedPolicy(Properties properties, Path refPolicyPath) {
226         //
227         // Get the current set of referenced policy ids
228         //
229         StringJoiner join = new StringJoiner(",");
230         boolean found = false;
231         Set<String> referencedPolicies = XACMLProperties.getReferencedPolicyIDs(properties);
232         for (String refPolicy : referencedPolicies) {
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, refPolicyPath.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_REFERENCEDPOLICIES, join.toString());
257         }
258         return properties;
259     }
260
261     /**
262      * Does a debug dump of referenced and root policy values.
263      *
264      * @param properties Input Properties object
265      * @param logger Logger object to use
266      */
267     public static void debugDumpPolicyProperties(Properties properties, Logger logger) {
268         //
269         // I hate surrounding this all with an if, but by
270         // doing so I clear sonar issues with passing System.lineSeparator()
271         // as an argument.
272         //
273         if (logger.isDebugEnabled()) {
274             //
275             // Get the current set of referenced policy ids
276             //
277             Set<String> rootPolicies = XACMLProperties.getRootPolicyIDs(properties);
278             logger.debug("Root Policies: {}", properties.getProperty(XACMLProperties.PROP_ROOTPOLICIES));
279             for (String root : rootPolicies) {
280                 logger.debug("{}", properties.getProperty(root + DOT_FILE_SUFFIX, NOT_FOUND_MESSAGE));
281             }
282             //
283             // Get the current set of referenced policy ids
284             //
285             Set<String> referencedPolicies = XACMLProperties.getReferencedPolicyIDs(properties);
286             logger.debug("Referenced Policies: {}", properties.getProperty(XACMLProperties.PROP_REFERENCEDPOLICIES));
287             for (String ref : referencedPolicies) {
288                 logger.debug("{}", properties.getProperty(ref + DOT_FILE_SUFFIX, NOT_FOUND_MESSAGE));
289             }
290         }
291     }
292
293     /**
294      * Constructs a unique policy filename for a given policy.
295      *
296      * <P>It could be dangerous to use policy-id and policy-version if the user
297      * gives us an invalid policy-id and policy-versions.
298      *
299      * <P>Should we append a UUID also to guarantee uniqueness?
300      *
301      * <P>How do we track that in case we need to know what policies we have loaded?
302      *
303      * @param policy PolicyType object
304      * @param path Path for policy
305      * @return Path unique file path for the Policy
306      */
307     public static Path constructUniquePolicyFilename(PolicyType policy, Path path) {
308         //
309         //
310         // Can it be possible to produce an invalid filename?
311         // Should we insert a UUID
312         //
313         String filename = policy.getPolicyId() + "_" + policy.getVersion() + ".xml";
314         //
315         // Construct the Path
316         //
317         return Paths.get(path.toAbsolutePath().toString(), filename);
318     }
319
320     /**
321      * Load properties from given file.
322      *
323      * @throws IOException If unable to read file
324      */
325     public static Properties loadXacmlProperties(Path propertyPath) throws IOException {
326         LOGGER.debug("Loading xacml properties {}", propertyPath);
327         try (InputStream is = Files.newInputStream(propertyPath)) {
328             Properties properties = new Properties();
329             properties.load(is);
330             if (LOGGER.isDebugEnabled()) {
331                 LOGGER.debug("Loaded xacml properties {} {}", System.lineSeparator(), properties);
332                 for (Entry<Object, Object> entrySet : properties.entrySet()) {
333                     LOGGER.debug("{} -> {}", entrySet.getKey(), entrySet.getValue());
334                 }
335             }
336             return properties;
337         }
338     }
339
340     /**
341      * Stores the XACML Properties to the given file location.
342      *
343      * @throws IOException If unable to store the file.
344      */
345     public static void storeXacmlProperties(Properties properties, Path propertyPath) throws IOException {
346         if (LOGGER.isDebugEnabled()) {
347             LOGGER.debug("Storing xacml properties {} {} {}", properties, System.lineSeparator(), propertyPath);
348         }
349         try (OutputStream os = Files.newOutputStream(propertyPath)) {
350             String strComments = "#";
351             properties.store(os, strComments);
352         }
353     }
354
355     /**
356      * Appends 'xacml.properties' to a root Path object
357      *
358      * @param rootPath Root Path object
359      * @return Path to rootPath/xacml.properties file
360      */
361     public static Path getPropertiesPath(Path rootPath) {
362         return Paths.get(rootPath.toAbsolutePath().toString(), "xacml.properties");
363     }
364
365     @FunctionalInterface
366     public interface FileCreator {
367         public File createAFile(String filename) throws IOException;
368
369     }
370
371     /**
372      * Copies a xacml.properties file to another location and all the policies defined within it.
373      *
374      * @param propertiesPath Path to an existing properties file
375      * @param properties Properties object
376      * @param creator A callback that can create files. Allows JUnit test to pass Temporary folder
377      * @return File object that points to new Properties file
378      * @throws IOException Could not read/write files
379      */
380     public static File copyXacmlPropertiesContents(String propertiesPath, Properties properties,
381             FileCreator creator) throws IOException {
382         //
383         // Open the properties file
384         //
385         try (InputStream is = new FileInputStream(propertiesPath)) {
386             //
387             // Load in the properties
388             //
389             properties.load(is);
390             //
391             // Now we create a new xacml.properties in the temporary folder location
392             //
393             File propertiesFile = creator.createAFile("xacml.properties");
394             //
395             // Iterate through any root policies defined
396             //
397             for (String root : XACMLProperties.getRootPolicyIDs(properties)) {
398                 //
399                 // Get a file
400                 //
401                 Path rootPath = Paths.get(properties.getProperty(root + DOT_FILE_SUFFIX));
402                 LOGGER.debug("Root file {} {}", rootPath, rootPath.getFileName());
403                 //
404                 // Construct new path for the root policy
405                 //
406                 File newRootPath = creator.createAFile(rootPath.getFileName().toString());
407                 //
408                 // Copy the policy file to the temporary folder
409                 //
410                 com.google.common.io.Files.copy(rootPath.toFile(), newRootPath);
411                 //
412                 // Change the properties object to point to where the new policy is
413                 // in the temporary folder
414                 //
415                 properties.setProperty(root + DOT_FILE_SUFFIX, newRootPath.getAbsolutePath());
416             }
417             //
418             // Iterate through any referenced policies defined
419             //
420             for (String referenced : XACMLProperties.getReferencedPolicyIDs(properties)) {
421                 //
422                 // Get a file
423                 //
424                 Path refPath = Paths.get(properties.getProperty(referenced + DOT_FILE_SUFFIX));
425                 LOGGER.debug("Referenced file {} {}", refPath, refPath.getFileName());
426                 //
427                 // Construct new path for the root policy
428                 //
429                 File newReferencedPath = creator.createAFile(refPath.getFileName().toString());
430                 //
431                 // Copy the policy file to the temporary folder
432                 //
433                 com.google.common.io.Files.copy(refPath.toFile(), newReferencedPath);
434                 //
435                 // Change the properties object to point to where the new policy is
436                 // in the temporary folder
437                 //
438                 properties.setProperty(referenced + DOT_FILE_SUFFIX, newReferencedPath.getAbsolutePath());
439             }
440             //
441             // Save the new properties file to the temporary folder
442             //
443             try (OutputStream os = new FileOutputStream(propertiesFile.getAbsolutePath())) {
444                 properties.store(os, "");
445             }
446             //
447             // Return the new path to the properties folder
448             //
449             return propertiesFile;
450         }
451     }
452 }