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.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;
40 import java.util.StringJoiner;
41 import java.util.function.Function;
42 import java.util.stream.Collectors;
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 import org.slf4j.Logger;
49 import org.slf4j.LoggerFactory;
51 public class XacmlPolicyUtils {
53 private static final Logger LOGGER = LoggerFactory.getLogger(XacmlPolicyUtils.class);
55 public static final String XACML_PROPERTY_FILE = "xacml.properties";
56 public static final String LINE_SEPARATOR = System.lineSeparator();
58 private static final String DOT_FILE_SUFFIX = ".file";
59 private static final String NOT_FOUND_MESSAGE = "NOT FOUND";
62 * Function that sanitizes a file name, if the OS is Windows, so that it's a valid
63 * file name. Does nothing for other OSs.
65 private static final Function<String, String> SANITIZE_FILE_NAME =
66 System.getProperty("os.name").startsWith("Windows")
67 ? filename -> filename.replace(':', '_')
68 : filename -> filename;
70 private XacmlPolicyUtils() {
75 * Creates an empty PolicySetType object given the id and combining algorithm. Note,there
76 * will also be an empty Target created. You can easily override that if need be.
78 * @param policyId Policy Id
79 * @param policyCombiningAlgorithm Policy Combining Algorithm
80 * @return PolicySetType object
82 public static PolicySetType createEmptyPolicySet(String policyId, Identifier policyCombiningAlgorithm) {
83 PolicySetType policy = new PolicySetType();
84 policy.setPolicySetId(policyId);
85 policy.setPolicyCombiningAlgId(policyCombiningAlgorithm.stringValue());
86 policy.setTarget(new TargetType());
91 * Creates an empty PolicySetType object given the id and combining algorithm. Note,there
92 * will also be an empty Target created. You can easily override that if need be.
94 * @param policyId Policy Id
95 * @param ruleCombiningAlgorithm Rule Combining Algorithm
96 * @return PolicyType object
98 public static PolicyType createEmptyPolicy(String policyId, Identifier ruleCombiningAlgorithm) {
99 PolicyType policy = new PolicyType();
100 policy.setPolicyId(policyId);
101 policy.setRuleCombiningAlgId(ruleCombiningAlgorithm.stringValue());
102 policy.setTarget(new TargetType());
107 * This method adds a list of PolicyType objects to a root PolicySetType as
108 * referenced policies.
110 * @param rootPolicy Root PolicySet being updated
111 * @param referencedPolicies A list of PolicyType being added as a references
112 * @return the rootPolicy PolicySet object
114 public static PolicySetType addPoliciesToXacmlRootPolicy(PolicySetType rootPolicy,
115 PolicyType... referencedPolicies) {
116 ObjectFactory factory = new ObjectFactory();
118 // Iterate each policy
120 for (PolicyType referencedPolicy : referencedPolicies) {
121 IdReferenceType reference = new IdReferenceType();
122 reference.setValue(referencedPolicy.getPolicyId());
126 rootPolicy.getPolicySetOrPolicyOrPolicySetIdReference().add(factory.createPolicyIdReference(reference));
129 // Return the updated object
135 * This method updates a root PolicySetType by adding in a PolicyType as a reference.
137 * @param rootPolicy Root PolicySet being updated
138 * @param referencedPolicySets A list of PolicySetType being added as a references
139 * @return the rootPolicy PolicySet object
141 public static PolicySetType addPolicySetsToXacmlRootPolicy(PolicySetType rootPolicy,
142 PolicySetType... referencedPolicySets) {
143 ObjectFactory factory = new ObjectFactory();
145 // Iterate each policy
147 for (PolicySetType referencedPolicySet : referencedPolicySets) {
148 IdReferenceType reference = new IdReferenceType();
149 reference.setValue(referencedPolicySet.getPolicySetId());
153 rootPolicy.getPolicySetOrPolicyOrPolicySetIdReference().add(factory.createPolicySetIdReference(reference));
156 // Return the updated object
162 * Adds in the root policy to the PDP properties object.
164 * @param properties Input properties
165 * @param rootPolicyPath Path to the root policy file
166 * @return Properties object
168 public static Properties addRootPolicy(Properties properties, Path rootPolicyPath) {
170 // Get the current set of referenced policy ids
172 Set<String> rootPolicies = XACMLProperties.getRootPolicyIDs(properties);
174 // Construct a unique id
178 String refId = "root" + id;
179 if (rootPolicies.contains(refId)) {
182 rootPolicies.add(refId);
183 properties.put(refId + DOT_FILE_SUFFIX, rootPolicyPath.toAbsolutePath().toString());
188 // Set the new comma separated list
190 properties.setProperty(XACMLProperties.PROP_ROOTPOLICIES,
191 rootPolicies.stream().collect(Collectors.joining(",")));
196 * Adds in the referenced policy to the PDP properties object.
198 * @param properties Input properties
199 * @param refPolicyPath Path to the referenced policy file
200 * @return Properties object
202 public static Properties addReferencedPolicy(Properties properties, Path refPolicyPath) {
204 // Get the current set of referenced policy ids
206 Set<String> referencedPolicies = XACMLProperties.getReferencedPolicyIDs(properties);
208 // Construct a unique id
212 String refId = "ref" + id;
213 if (referencedPolicies.contains(refId)) {
216 referencedPolicies.add(refId);
217 properties.put(refId + DOT_FILE_SUFFIX, refPolicyPath.toAbsolutePath().toString());
222 // Set the new comma separated list
224 properties.setProperty(XACMLProperties.PROP_REFERENCEDPOLICIES,
225 referencedPolicies.stream().collect(Collectors.joining(",")));
230 * Removes a root policy from the Properties object. Both in the line
231 * that identifies the policy and the .file property that points to the path.
233 * @param properties Input Properties object to remove
234 * @param rootPolicyPath The policy file path
235 * @return Properties object
237 public static Properties removeRootPolicy(Properties properties, Path rootPolicyPath) {
239 // Get the current set of referenced policy ids
241 StringJoiner join = new StringJoiner(",");
242 boolean found = false;
243 Set<String> rootPolicies = XACMLProperties.getRootPolicyIDs(properties);
244 for (String refPolicy : rootPolicies) {
245 String refPolicyFile = refPolicy + DOT_FILE_SUFFIX;
247 // If the key and value match, then it will return true
249 if (properties.remove(refPolicyFile, rootPolicyPath.toString())) {
251 // Record that we actually removed it
266 // Now update the list of referenced properties
268 properties.setProperty(XACMLProperties.PROP_ROOTPOLICIES, join.toString());
274 * Removes a referenced policy from the Properties object. Both in the line
275 * that identifies the policy and the .file property that points to the path.
277 * @param properties Input Properties object to remove
278 * @param refPolicyPath The policy file path
279 * @return Properties object
281 public static Properties removeReferencedPolicy(Properties properties, Path refPolicyPath) {
283 // Get the current set of referenced policy ids
285 StringJoiner join = new StringJoiner(",");
286 boolean found = false;
287 Set<String> referencedPolicies = XACMLProperties.getReferencedPolicyIDs(properties);
288 for (String refPolicy : referencedPolicies) {
289 String refPolicyFile = refPolicy + DOT_FILE_SUFFIX;
291 // If the key and value match, then it will return true
293 if (properties.remove(refPolicyFile, refPolicyPath.toString())) {
295 // Record that we actually removed it
310 // Now update the list of referenced properties
312 properties.setProperty(XACMLProperties.PROP_REFERENCEDPOLICIES, join.toString());
318 * Does a debug dump of referenced and root policy values.
320 * @param properties Input Properties object
321 * @param logger Logger object to use
323 public static void debugDumpPolicyProperties(Properties properties, Logger logger) {
325 // I hate surrounding this all with an if, but by
326 // doing so I clear sonar issues with passing System.lineSeparator()
329 if (logger.isDebugEnabled()) {
331 // Get the current set of referenced policy ids
333 Set<String> rootPolicies = XACMLProperties.getRootPolicyIDs(properties);
334 logger.debug("Root Policies: {}", properties.getProperty(XACMLProperties.PROP_ROOTPOLICIES));
335 for (String root : rootPolicies) {
336 logger.debug("{}", properties.getProperty(root + DOT_FILE_SUFFIX, NOT_FOUND_MESSAGE));
339 // Get the current set of referenced policy ids
341 Set<String> referencedPolicies = XACMLProperties.getReferencedPolicyIDs(properties);
342 logger.debug("Referenced Policies: {}", properties.getProperty(XACMLProperties.PROP_REFERENCEDPOLICIES));
343 for (String ref : referencedPolicies) {
344 logger.debug("{}", properties.getProperty(ref + DOT_FILE_SUFFIX, NOT_FOUND_MESSAGE));
350 * Constructs a unique policy filename for a given policy.
352 * <P>It could be dangerous to use policy-id and policy-version if the user
353 * gives us an invalid policy-id and policy-versions.
355 * <P>Should we append a UUID also to guarantee uniqueness?
357 * <P>How do we track that in case we need to know what policies we have loaded?
359 * @param policy PolicyType object
360 * @param path Path for policy
361 * @return Path unique file path for the Policy
363 public static Path constructUniquePolicyFilename(Object policy, Path path) {
366 if (policy instanceof PolicyType) {
367 id = ((PolicyType) policy).getPolicyId();
368 version = ((PolicyType) policy).getVersion();
369 } else if (policy instanceof PolicySetType) {
370 id = ((PolicySetType) policy).getPolicySetId();
371 version = ((PolicySetType) policy).getVersion();
373 throw new IllegalArgumentException("Must pass a PolicyType or PolicySetType");
377 // Can it be possible to produce an invalid filename?
378 // Should we insert a UUID
380 String filename = id + "_" + version + ".xml";
382 // Construct the Path
384 return Paths.get(path.toAbsolutePath().toString(), SANITIZE_FILE_NAME.apply(filename));
388 * Load properties from given file.
390 * @throws IOException If unable to read file
392 public static Properties loadXacmlProperties(Path propertyPath) throws IOException {
393 LOGGER.info("Loading xacml properties {}", propertyPath);
394 try (InputStream is = Files.newInputStream(propertyPath)) {
395 Properties properties = new Properties();
397 if (LOGGER.isInfoEnabled()) {
398 LOGGER.info("Loaded xacml properties {} {}", XacmlPolicyUtils.LINE_SEPARATOR, properties);
399 for (Entry<Object, Object> entrySet : properties.entrySet()) {
400 LOGGER.info("{} -> {}", entrySet.getKey(), entrySet.getValue());
408 * Stores the XACML Properties to the given file location.
410 * @throws IOException If unable to store the file.
412 public static void storeXacmlProperties(Properties properties, Path propertyPath) throws IOException {
413 LOGGER.info("Storing xacml properties {} {} {}", properties, XacmlPolicyUtils.LINE_SEPARATOR, propertyPath);
414 try (OutputStream os = Files.newOutputStream(propertyPath)) {
415 String strComments = "#";
416 properties.store(os, strComments);
421 * Appends 'xacml.properties' to a root Path object
423 * @param rootPath Root Path object
424 * @return Path to rootPath/xacml.properties file
426 public static Path getPropertiesPath(Path rootPath) {
427 return Paths.get(rootPath.toAbsolutePath().toString(), XACML_PROPERTY_FILE);
431 public interface FileCreator {
432 public File createAFile(String filename) throws IOException;
437 * Copies a xacml.properties file to another location and all the policies defined within it.
439 * @param propertiesPath Path to an existing properties file
440 * @param properties Properties object
441 * @param creator A callback that can create files. Allows JUnit test to pass Temporary folder
442 * @return File object that points to new Properties file
443 * @throws IOException Could not read/write files
445 public static File copyXacmlPropertiesContents(String propertiesPath, Properties properties,
446 FileCreator creator) throws IOException {
448 // Open the properties file
450 try (InputStream is = new FileInputStream(propertiesPath)) {
452 // Load in the properties
456 // Now we create a new xacml.properties in the temporary folder location
458 File propertiesFile = creator.createAFile(XACML_PROPERTY_FILE);
460 // Iterate through any root policies defined
462 for (String root : XACMLProperties.getRootPolicyIDs(properties)) {
466 Path rootPath = Paths.get(properties.getProperty(root + DOT_FILE_SUFFIX));
467 LOGGER.info("Root file {} {}", rootPath, rootPath.getFileName());
469 // Construct new path for the root policy
471 File newRootPath = creator.createAFile(rootPath.getFileName().toString());
473 // Copy the policy file to the temporary folder
475 com.google.common.io.Files.copy(rootPath.toFile(), newRootPath);
477 // Change the properties object to point to where the new policy is
478 // in the temporary folder
480 properties.setProperty(root + DOT_FILE_SUFFIX, newRootPath.getAbsolutePath());
483 // Iterate through any referenced policies defined
485 for (String referenced : XACMLProperties.getReferencedPolicyIDs(properties)) {
489 Path refPath = Paths.get(properties.getProperty(referenced + DOT_FILE_SUFFIX));
490 LOGGER.info("Referenced file {} {}", refPath, refPath.getFileName());
492 // Construct new path for the root policy
494 File newReferencedPath = creator.createAFile(refPath.getFileName().toString());
496 // Copy the policy file to the temporary folder
498 com.google.common.io.Files.copy(refPath.toFile(), newReferencedPath);
500 // Change the properties object to point to where the new policy is
501 // in the temporary folder
503 properties.setProperty(referenced + DOT_FILE_SUFFIX, newReferencedPath.getAbsolutePath());
506 // Save the new properties file to the temporary folder
508 try (OutputStream os = new FileOutputStream(propertiesFile.getAbsolutePath())) {
509 properties.store(os, "");
512 // Return the new path to the properties folder
514 return propertiesFile;
519 * Wraps the call to XACMLPolicyWriter.
521 * @param path Path to file to be written to.
522 * @param policy PolicyType or PolicySetType
523 * @return Path - the same path passed in most likely from XACMLPolicyWriter. Or NULL if an error occurs.
525 public static Path writePolicyFile(Path path, Object policy) {
526 if (policy instanceof PolicyType) {
527 return XACMLPolicyWriter.writePolicyFile(path, (PolicyType) policy);
528 } else if (policy instanceof PolicySetType) {
529 return XACMLPolicyWriter.writePolicyFile(path, (PolicySetType) policy);
531 throw new IllegalArgumentException("Expecting PolicyType or PolicySetType");