2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2019-2021 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
11 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 * SPDX-License-Identifier: Apache-2.0
20 * ============LICENSE_END=========================================================
23 package org.onap.policy.pdp.xacml.application.common;
25 import com.att.research.xacml.api.Identifier;
26 import com.att.research.xacml.util.XACMLPolicyWriter;
27 import com.att.research.xacml.util.XACMLProperties;
29 import java.io.FileInputStream;
30 import java.io.FileOutputStream;
31 import java.io.IOException;
32 import java.io.OutputStream;
33 import java.nio.file.Files;
34 import java.nio.file.Path;
35 import java.nio.file.Paths;
36 import java.util.Map.Entry;
37 import java.util.Properties;
39 import java.util.StringJoiner;
40 import java.util.function.Function;
41 import java.util.stream.Collectors;
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 import org.slf4j.Logger;
48 import org.slf4j.LoggerFactory;
50 public class XacmlPolicyUtils {
52 private static final Logger LOGGER = LoggerFactory.getLogger(XacmlPolicyUtils.class);
54 public static final String XACML_PROPERTY_FILE = "xacml.properties";
55 public static final String LINE_SEPARATOR = System.lineSeparator();
57 private static final String DOT_FILE_SUFFIX = ".file";
58 private static final String NOT_FOUND_MESSAGE = "NOT FOUND";
61 * Function that sanitizes a file name, if the OS is Windows, so that it's a valid
62 * file name. Does nothing for other OSs.
64 private static final Function<String, String> SANITIZE_FILE_NAME =
65 System.getProperty("os.name").startsWith("Windows")
66 ? filename -> filename.replace(':', '_')
67 : filename -> filename;
69 private XacmlPolicyUtils() {
74 * Creates an empty PolicySetType object given the id and combining algorithm. Note,there
75 * will also be an empty Target created. You can easily override that if need be.
77 * @param policyId Policy Id
78 * @param policyCombiningAlgorithm Policy Combining Algorithm
79 * @return PolicySetType object
81 public static PolicySetType createEmptyPolicySet(String policyId, Identifier policyCombiningAlgorithm) {
82 var policy = new PolicySetType();
83 policy.setPolicySetId(policyId);
84 policy.setPolicyCombiningAlgId(policyCombiningAlgorithm.stringValue());
85 policy.setTarget(new TargetType());
90 * Creates an empty PolicySetType object given the id and combining algorithm. Note,there
91 * will also be an empty Target created. You can easily override that if need be.
93 * @param policyId Policy Id
94 * @param ruleCombiningAlgorithm Rule Combining Algorithm
95 * @return PolicyType object
97 public static PolicyType createEmptyPolicy(String policyId, Identifier ruleCombiningAlgorithm) {
98 var policy = new PolicyType();
99 policy.setPolicyId(policyId);
100 policy.setRuleCombiningAlgId(ruleCombiningAlgorithm.stringValue());
101 policy.setTarget(new TargetType());
106 * This method adds a list of PolicyType objects to a root PolicySetType as
107 * referenced policies.
109 * @param rootPolicy Root PolicySet being updated
110 * @param referencedPolicies A list of PolicyType being added as a references
111 * @return the rootPolicy PolicySet object
113 public static PolicySetType addPoliciesToXacmlRootPolicy(PolicySetType rootPolicy,
114 PolicyType... referencedPolicies) {
115 var factory = new ObjectFactory();
117 // Iterate each policy
119 for (PolicyType referencedPolicy : referencedPolicies) {
120 var reference = new IdReferenceType();
121 reference.setValue(referencedPolicy.getPolicyId());
125 rootPolicy.getPolicySetOrPolicyOrPolicySetIdReference().add(factory.createPolicyIdReference(reference));
128 // Return the updated object
134 * This method updates a root PolicySetType by adding in a PolicyType as a reference.
136 * @param rootPolicy Root PolicySet being updated
137 * @param referencedPolicySets A list of PolicySetType being added as a references
138 * @return the rootPolicy PolicySet object
140 public static PolicySetType addPolicySetsToXacmlRootPolicy(PolicySetType rootPolicy,
141 PolicySetType... referencedPolicySets) {
142 var factory = new ObjectFactory();
144 // Iterate each policy
146 for (PolicySetType referencedPolicySet : referencedPolicySets) {
147 var reference = new IdReferenceType();
148 reference.setValue(referencedPolicySet.getPolicySetId());
152 rootPolicy.getPolicySetOrPolicyOrPolicySetIdReference().add(factory.createPolicySetIdReference(reference));
155 // Return the updated object
161 * Adds in the root policy to the PDP properties object.
163 * @param properties Input properties
164 * @param rootPolicyPath Path to the root policy file
165 * @return Properties object
167 public static Properties addRootPolicy(Properties properties, Path rootPolicyPath) {
169 // Get the current set of referenced policy ids
171 Set<String> rootPolicies = XACMLProperties.getRootPolicyIDs(properties);
173 // Construct a unique id
177 String refId = "root" + id;
178 if (rootPolicies.contains(refId)) {
181 rootPolicies.add(refId);
182 properties.put(refId + DOT_FILE_SUFFIX, rootPolicyPath.toAbsolutePath().toString());
187 // Set the new comma separated list
189 properties.setProperty(XACMLProperties.PROP_ROOTPOLICIES,
190 rootPolicies.stream().collect(Collectors.joining(",")));
195 * Adds in the referenced policy to the PDP properties object.
197 * @param properties Input properties
198 * @param refPolicyPath Path to the referenced policy file
199 * @return Properties object
201 public static Properties addReferencedPolicy(Properties properties, Path refPolicyPath) {
203 // Get the current set of referenced policy ids
205 Set<String> referencedPolicies = XACMLProperties.getReferencedPolicyIDs(properties);
207 // Construct a unique id
211 String refId = "ref" + id;
212 if (referencedPolicies.contains(refId)) {
215 referencedPolicies.add(refId);
216 properties.put(refId + DOT_FILE_SUFFIX, refPolicyPath.toAbsolutePath().toString());
221 // Set the new comma separated list
223 properties.setProperty(XACMLProperties.PROP_REFERENCEDPOLICIES,
224 referencedPolicies.stream().collect(Collectors.joining(",")));
229 * Removes a root policy from the Properties object. Both in the line
230 * that identifies the policy and the .file property that points to the path.
232 * @param properties Input Properties object to remove
233 * @param rootPolicyPath The policy file path
234 * @return Properties object
236 public static Properties removeRootPolicy(Properties properties, Path rootPolicyPath) {
238 // Get the current set of referenced policy ids
240 var join = new StringJoiner(",");
242 Set<String> rootPolicies = XACMLProperties.getRootPolicyIDs(properties);
243 for (String refPolicy : rootPolicies) {
244 String refPolicyFile = refPolicy + DOT_FILE_SUFFIX;
246 // If the key and value match, then it will return true
248 if (properties.remove(refPolicyFile, rootPolicyPath.toString())) {
250 // Record that we actually removed it
265 // Now update the list of referenced properties
267 properties.setProperty(XACMLProperties.PROP_ROOTPOLICIES, join.toString());
273 * Removes a referenced policy from the Properties object. Both in the line
274 * that identifies the policy and the .file property that points to the path.
276 * @param properties Input Properties object to remove
277 * @param refPolicyPath The policy file path
278 * @return Properties object
280 public static Properties removeReferencedPolicy(Properties properties, Path refPolicyPath) {
282 // Get the current set of referenced policy ids
284 var join = new StringJoiner(",");
286 Set<String> referencedPolicies = XACMLProperties.getReferencedPolicyIDs(properties);
287 for (String refPolicy : referencedPolicies) {
288 String refPolicyFile = refPolicy + DOT_FILE_SUFFIX;
290 // If the key and value match, then it will return true
292 if (properties.remove(refPolicyFile, refPolicyPath.toString())) {
294 // Record that we actually removed it
309 // Now update the list of referenced properties
311 properties.setProperty(XACMLProperties.PROP_REFERENCEDPOLICIES, join.toString());
317 * Does a debug dump of referenced and root policy values.
319 * @param properties Input Properties object
320 * @param logger Logger object to use
322 public static void debugDumpPolicyProperties(Properties properties, Logger logger) {
324 // I hate surrounding this all with an if, but by
325 // doing so I clear sonar issues with passing System.lineSeparator()
328 if (logger.isDebugEnabled()) {
330 // Get the current set of referenced policy ids
332 Set<String> rootPolicies = XACMLProperties.getRootPolicyIDs(properties);
333 logger.debug("Root Policies: {}", properties.getProperty(XACMLProperties.PROP_ROOTPOLICIES));
334 for (String root : rootPolicies) {
335 logger.debug("{}", properties.getProperty(root + DOT_FILE_SUFFIX, NOT_FOUND_MESSAGE));
338 // Get the current set of referenced policy ids
340 Set<String> referencedPolicies = XACMLProperties.getReferencedPolicyIDs(properties);
341 logger.debug("Referenced Policies: {}", properties.getProperty(XACMLProperties.PROP_REFERENCEDPOLICIES));
342 for (String ref : referencedPolicies) {
343 logger.debug("{}", properties.getProperty(ref + DOT_FILE_SUFFIX, NOT_FOUND_MESSAGE));
349 * Constructs a unique policy filename for a given policy.
351 * <P>It could be dangerous to use policy-id and policy-version if the user
352 * gives us an invalid policy-id and policy-versions.
354 * <P>Should we append a UUID also to guarantee uniqueness?
356 * <P>How do we track that in case we need to know what policies we have loaded?
358 * @param policy PolicyType object
359 * @param path Path for policy
360 * @return Path unique file path for the Policy
362 public static Path constructUniquePolicyFilename(Object policy, Path path) {
365 if (policy instanceof PolicyType) {
366 id = ((PolicyType) policy).getPolicyId();
367 version = ((PolicyType) policy).getVersion();
368 } else if (policy instanceof PolicySetType) {
369 id = ((PolicySetType) policy).getPolicySetId();
370 version = ((PolicySetType) policy).getVersion();
372 throw new IllegalArgumentException("Must pass a PolicyType or PolicySetType");
376 // Can it be possible to produce an invalid filename?
377 // Should we insert a UUID
379 String filename = id + "_" + version + ".xml";
381 // Construct the Path
383 return Paths.get(path.toAbsolutePath().toString(), SANITIZE_FILE_NAME.apply(filename));
387 * Load properties from given file.
389 * @throws IOException If unable to read file
391 public static Properties loadXacmlProperties(Path propertyPath) throws IOException {
392 LOGGER.info("Loading xacml properties {}", propertyPath);
393 try (var is = Files.newInputStream(propertyPath)) {
394 var properties = new Properties();
396 if (LOGGER.isInfoEnabled()) {
397 LOGGER.info("Loaded xacml properties {} {}", XacmlPolicyUtils.LINE_SEPARATOR, properties);
398 for (Entry<Object, Object> entrySet : properties.entrySet()) {
399 LOGGER.info("{} -> {}", entrySet.getKey(), entrySet.getValue());
407 * Stores the XACML Properties to the given file location.
409 * @throws IOException If unable to store the file.
411 public static void storeXacmlProperties(Properties properties, Path propertyPath) throws IOException {
412 LOGGER.info("Storing xacml properties {} {} {}", properties, XacmlPolicyUtils.LINE_SEPARATOR, propertyPath);
413 try (var os = Files.newOutputStream(propertyPath)) {
414 properties.store(os, "#");
419 * Appends 'xacml.properties' to a root Path object
421 * @param rootPath Root Path object
422 * @return Path to rootPath/xacml.properties file
424 public static Path getPropertiesPath(Path rootPath) {
425 return Paths.get(rootPath.toAbsolutePath().toString(), XACML_PROPERTY_FILE);
429 public interface FileCreator {
430 public File createAFile(String filename) throws IOException;
435 * Copies a xacml.properties file to another location and all the policies defined within it.
437 * @param propertiesPath Path to an existing properties file
438 * @param properties Properties object
439 * @param creator A callback that can create files. Allows JUnit test to pass Temporary folder
440 * @return File object that points to new Properties file
441 * @throws IOException Could not read/write files
443 public static File copyXacmlPropertiesContents(String propertiesPath, Properties properties,
444 FileCreator creator) throws IOException {
446 // Open the properties file
448 try (var is = new FileInputStream(propertiesPath)) {
450 // Load in the properties
454 // Now we create a new xacml.properties in the temporary folder location
456 var propertiesFile = creator.createAFile(XACML_PROPERTY_FILE);
458 // Iterate through any root policies defined
460 for (String root : XACMLProperties.getRootPolicyIDs(properties)) {
464 var rootPath = Paths.get(properties.getProperty(root + DOT_FILE_SUFFIX));
465 LOGGER.info("Root file {} {}", rootPath, rootPath.getFileName());
467 // Construct new path for the root policy
469 var newRootPath = creator.createAFile(rootPath.getFileName().toString());
471 // Copy the policy file to the temporary folder
473 com.google.common.io.Files.copy(rootPath.toFile(), newRootPath);
475 // Change the properties object to point to where the new policy is
476 // in the temporary folder
478 properties.setProperty(root + DOT_FILE_SUFFIX, newRootPath.getAbsolutePath());
481 // Iterate through any referenced policies defined
483 for (String referenced : XACMLProperties.getReferencedPolicyIDs(properties)) {
487 var refPath = Paths.get(properties.getProperty(referenced + DOT_FILE_SUFFIX));
488 LOGGER.info("Referenced file {} {}", refPath, refPath.getFileName());
490 // Construct new path for the root policy
492 var newReferencedPath = creator.createAFile(refPath.getFileName().toString());
494 // Copy the policy file to the temporary folder
496 com.google.common.io.Files.copy(refPath.toFile(), newReferencedPath);
498 // Change the properties object to point to where the new policy is
499 // in the temporary folder
501 properties.setProperty(referenced + DOT_FILE_SUFFIX, newReferencedPath.getAbsolutePath());
504 // Save the new properties file to the temporary folder
506 try (OutputStream os = new FileOutputStream(propertiesFile.getAbsolutePath())) {
507 properties.store(os, "");
510 // Return the new path to the properties folder
512 return propertiesFile;
517 * Wraps the call to XACMLPolicyWriter.
519 * @param path Path to file to be written to.
520 * @param policy PolicyType or PolicySetType
521 * @return Path - the same path passed in most likely from XACMLPolicyWriter. Or NULL if an error occurs.
523 public static Path writePolicyFile(Path path, Object policy) {
524 if (policy instanceof PolicyType) {
525 return XACMLPolicyWriter.writePolicyFile(path, (PolicyType) policy);
526 } else if (policy instanceof PolicySetType) {
527 return XACMLPolicyWriter.writePolicyFile(path, (PolicySetType) policy);
529 throw new IllegalArgumentException("Expecting PolicyType or PolicySetType");