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