2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2017-2018 AT&T Intellectual Property. All rights reserved.
6 * Modifications Copyright (C) 2019 Nordix Foundation.
7 * ================================================================================
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
12 * http://www.apache.org/licenses/LICENSE-2.0
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 * ============LICENSE_END=========================================================
22 package org.onap.policy.pdp.rest;
24 import com.att.research.xacml.api.pap.PAPException;
25 import com.att.research.xacml.api.pap.PDPStatus;
26 import com.att.research.xacml.api.pap.PDPStatus.Status;
27 import com.att.research.xacml.api.pdp.PDPEngine;
28 import com.att.research.xacml.api.pdp.PDPEngineFactory;
29 import com.att.research.xacml.api.pip.PIPEngine;
30 import com.att.research.xacml.api.pip.PIPException;
31 import com.att.research.xacml.api.pip.PIPFinder;
32 import com.att.research.xacml.api.pip.PIPFinderFactory;
33 import com.att.research.xacml.util.FactoryException;
34 import com.att.research.xacml.util.XACMLProperties;
35 import com.att.research.xacmlatt.pdp.policy.PolicyDef;
36 import com.att.research.xacmlatt.pdp.policy.dom.DOMPolicyDef;
37 import com.att.research.xacmlatt.pdp.std.StdPolicyFinderFactory;
38 import com.google.common.base.Splitter;
40 import java.io.FileOutputStream;
41 import java.io.IOException;
42 import java.io.InputStream;
43 import java.io.OutputStream;
44 import java.net.MalformedURLException;
47 import java.net.URLConnection;
48 import java.nio.charset.StandardCharsets;
49 import java.nio.file.Files;
50 import java.nio.file.Path;
51 import java.nio.file.Paths;
52 import java.util.Base64;
53 import java.util.ConcurrentModificationException;
54 import java.util.HashMap;
55 import java.util.Properties;
58 import org.apache.commons.io.IOUtils;
59 import org.onap.policy.common.logging.flexlogger.FlexLogger;
60 import org.onap.policy.common.logging.flexlogger.Logger;
61 import org.onap.policy.pdp.rest.notifications.NotificationController;
62 import org.onap.policy.rest.XacmlRest;
63 import org.onap.policy.rest.XacmlRestProperties;
64 import org.onap.policy.xacml.api.XACMLErrorConstants;
65 import org.onap.policy.xacml.std.pap.StdPDPPIPConfig;
66 import org.onap.policy.xacml.std.pap.StdPDPPolicy;
67 import org.onap.policy.xacml.std.pap.StdPDPStatus;
70 * Does the work for loading policy and PIP configurations sent from the PAP servlet.
72 public class XACMLPdpLoader {
73 private static final Logger LOGGER = FlexLogger.getLogger(XACMLPdpLoader.class);
75 // Repeated string constants.
76 private static final String DOES_NOT_EXIST = " does NOT exist.";
77 private static final String CORRUPTED_POLICY_FILE_DELETING = "Corrupted policy file, deleting: ";
78 private static final String DOT_FILE = ".file";
80 private static NotificationController notificationController = new NotificationController();
81 private static final Long notifyDelay = (long) XACMLPdpServlet.getNotificationDelay();
83 public static synchronized PDPEngine loadEngine(StdPDPStatus status, Properties policyProperties,
84 Properties pipProperties) {
85 LOGGER.info("loadEngine: " + policyProperties + " " + pipProperties);
87 // First load our policies
91 // Were we given some properties?
93 if (policyProperties == null) {
95 // On init we have no incoming configuration, so just
96 // Load our current saved configuration
98 policyProperties = new Properties();
99 try (InputStream is = Files.newInputStream(getPDPPolicyCache())) {
100 policyProperties.load(is);
105 // Get our policy cache up-to-date
107 // Side effects of this include:
108 // - downloading of policies from remote locations, and
109 // - creating new "<PolicyId>.file" properties for files existing
112 LOGGER.info("XACMLPdpLoader: cache the policies.");
113 XACMLPdpLoader.cachePolicies(policyProperties);
115 // Validate the policies
117 LOGGER.info("XACMLPdpLoader: validating the policies.");
118 XACMLPdpLoader.validatePolicies(policyProperties, status);
119 if (LOGGER.isDebugEnabled()) {
120 LOGGER.debug("Status: " + status);
122 } catch (ConcurrentModificationException e) {
123 LOGGER.error(XACMLErrorConstants.ERROR_PROCESS_FLOW + e.getMessage() + e);
124 } catch (Exception e) {
125 String error = "Failed to load Policy Cache properties file: " + e.getMessage();
126 LOGGER.error(XACMLErrorConstants.ERROR_PROCESS_FLOW + error, e);
127 status.addLoadError(error);
128 status.setStatus(PDPStatus.Status.LOAD_ERRORS);
131 // Load our PIP configuration
135 // Were we given some properties to use?
137 if (pipProperties == null) {
139 // Load our current saved configuration
141 pipProperties = new Properties();
142 try (InputStream is = Files.newInputStream(getPIPConfig())) {
143 pipProperties.load(is);
147 // Validate our PIP configurations
149 XACMLPdpLoader.validatePipConfiguration(pipProperties, status);
150 if (LOGGER.isDebugEnabled()) {
151 LOGGER.debug("Status: " + status);
153 } catch (Exception e) {
154 String error = "Failed to load/validate Pip Config properties file: " + e.getMessage();
155 LOGGER.error(XACMLErrorConstants.ERROR_DATA_ISSUE + error, e);
156 status.addLoadError(XACMLErrorConstants.ERROR_PROCESS_FLOW + error);
157 status.setStatus(PDPStatus.Status.LOAD_ERRORS);
160 // Were they validated?
162 if (status.getStatus() == Status.LOAD_ERRORS) {
163 LOGGER.error(XACMLErrorConstants.ERROR_PROCESS_FLOW + "there were load errors");
167 // Reset our official properties the PDP factory
168 // uses to configure the PDP engine.
170 XacmlRest.loadXacmlProperties(policyProperties, pipProperties);
172 // Dump ALL our properties that we are trying to load
175 LOGGER.info(XACMLProperties.getProperties().toString());
176 } catch (IOException e) {
177 LOGGER.error(XACMLErrorConstants.ERROR_PROCESS_FLOW + "Failed to get XACML Properties", e);
180 // Now load the PDP engine
182 PDPEngineFactory factory = null;
183 PDPEngine engine = null;
185 factory = PDPEngineFactory.newInstance();
186 engine = factory.newEngine();
187 LOGGER.info("Loaded new PDP engine.");
188 status.setStatus(Status.UP_TO_DATE);
189 } catch (FactoryException e) {
190 String error = "Failed to create new PDP Engine";
191 LOGGER.error(XACMLErrorConstants.ERROR_SYSTEM_ERROR + error, e);
192 status.addLoadError(error);
197 private static HashMap<String, PolicyDef> policyContainer = null;
200 * Thread for sending notifications.
202 public static synchronized void sendNotification() {
203 Thread notify = new Thread() {
207 Thread.sleep(notifyDelay);
208 NotificationController.sendNotification();
209 } catch (Exception e) {
210 LOGGER.error(XACMLErrorConstants.ERROR_UNKNOWN + e);
218 * Method to validate policies.
220 * @param properties The properties of the policies
221 * @param status the PDP status
222 * @throws PAPException on validation errors
224 public static synchronized void validatePolicies(Properties properties, StdPDPStatus status) throws PAPException {
225 Set<String> rootPolicies = XACMLProperties.getRootPolicyIDs(properties);
226 policyContainer = new HashMap<>();
228 LOGGER.info("XACMLPdpLoader: load rootPolicies");
229 for (String id : rootPolicies) {
230 loadPolicy(properties, status, id, true);
232 // remember which policies were root policies
233 status.addAllLoadedRootPolicies(status.getLoadedPolicies());
234 LOGGER.info("XACMLPdpLoader: load referencedPolicies");
235 Set<String> refPolicies = XACMLProperties.getReferencedPolicyIDs(properties);
236 for (String id : refPolicies) {
237 loadPolicy(properties, status, id, false);
239 LOGGER.info("Loaded " + status.getLoadedPolicies().size() + " policies, failed to load "
240 + status.getFailedPolicies().size() + " policies, " + status.getLoadedRootPolicies().size()
242 notificationController.check(status, policyContainer);
243 if (status.getLoadedRootPolicies().isEmpty()) {
245 XACMLErrorConstants.ERROR_PROCESS_FLOW + "NO ROOT POLICIES LOADED!!! Cannot serve PEP Requests.");
246 status.addLoadWarning("NO ROOT POLICIES LOADED!!! Cannot serve PEP Requests.");
248 policyContainer.clear();
254 * @param properties the policy properties
255 * @param status the PDP status
256 * @param id the policy ID
257 * @param isRoot indicates if operation being done as root
258 * @throws PAPException on loading errors
260 public static synchronized void loadPolicy(Properties properties, StdPDPStatus status, String id, boolean isRoot)
261 throws PAPException {
262 PolicyDef policy = null;
263 String location = null;
264 URI locationUri = null;
265 boolean isFile = false;
266 boolean rougeFile = false;
268 location = properties.getProperty(id + DOT_FILE);
269 if (location != null) {
271 locationUri = Paths.get(location).toUri();
272 try (InputStream is = Files.newInputStream(Paths.get(location))) {
273 policy = DOMPolicyDef.load(is);
274 } catch (Exception e) {
275 // This Happens if a any issue with the error policyFile. Lets remove it.
277 LOGGER.error(CORRUPTED_POLICY_FILE_DELETING + location + e);
278 Files.delete(Paths.get(location));
279 properties.remove(id + DOT_FILE);
281 } catch (IOException e1) {
286 if (location == null || rougeFile) {
290 location = properties.getProperty(id + ".url");
291 if (location != null) {
296 boolean error = false;
299 PapUrlResolver papUrls = PapUrlResolver.getInstance();
300 while (papUrls.hasMoreUrls()) {
301 String papID = papUrls.getUserId();
302 String papPass = papUrls.getPass();
303 Base64.Encoder encoder = Base64.getEncoder();
304 locationUri = URI.create(papUrls.getUrl(PapUrlResolver.extractIdFromUrl(location)));
305 URL url = locationUri.toURL();
306 URLConnection urlConnection = null;
308 urlConnection = url.openConnection();
309 } catch (IOException e) {
310 LOGGER.error("Exception Occured while opening connection" + e);
316 encoder.encodeToString((papID + ":" + papPass).getBytes(StandardCharsets.UTF_8));
317 urlConnection.setRequestProperty(XacmlRestProperties.PROP_PDP_HTTP_HEADER_ID,
318 XACMLProperties.getProperty(XacmlRestProperties.PROP_PDP_ID));
319 urlConnection.setRequestProperty("Authorization", "Basic " + encoding);
321 // Now construct the output file name
323 Path outFile = Paths.get(getPDPConfig().toAbsolutePath().toString(), id);
327 try (FileOutputStream fos = new FileOutputStream(outFile.toFile())) {
328 IOUtils.copy(urlConnection.getInputStream(), fos);
329 } catch (IOException e) {
330 LOGGER.error("Exception Occured while Copying input stream" + e);
339 try (InputStream fis = Files.newInputStream(outFile)) {
340 policy = DOMPolicyDef.load(fis);
341 } catch (Exception e) {
343 LOGGER.error(CORRUPTED_POLICY_FILE_DELETING + location + e);
344 Files.delete(outFile);
348 } catch (IOException e1) {
355 properties.setProperty(id + DOT_FILE, outFile.toAbsolutePath().toString());
360 while (error && errorCount > 2);
363 if (policy != null) {
364 status.addLoadedPolicy(new StdPDPPolicy(id, isRoot, locationUri, properties));
366 "Loaded policy: " + policy.getIdentifier() + " version: " + policy.getVersion().stringValue());
367 // Sending the policy objects to the Notification Controller.
368 policyContainer.put(id, policy);
370 String error = "Failed to load policy " + location;
371 LOGGER.error(XACMLErrorConstants.ERROR_PROCESS_FLOW + error);
372 status.setStatus(PDPStatus.Status.LOAD_ERRORS);
373 status.addLoadError(error);
374 status.addFailedPolicy(new StdPDPPolicy(id, isRoot));
376 } catch (Exception e) {
377 LOGGER.error(XACMLErrorConstants.ERROR_PROCESS_FLOW + "Failed to load policy '" + id + "' from location '"
378 + location + "'", e);
379 status.setStatus(PDPStatus.Status.LOAD_ERRORS);
380 status.addFailedPolicy(new StdPDPPolicy(id, isRoot));
390 XACMLErrorConstants.ERROR_PROCESS_FLOW + CORRUPTED_POLICY_FILE_DELETING + location);
391 Files.delete(Paths.get(location));
393 } catch (IOException e1) {
394 LOGGER.error(XACMLErrorConstants.ERROR_SYSTEM_ERROR + e1);
401 * Validate PIP configuration.
403 * @param properties the properties to validate
404 * @param status the PDP status
405 * @throws PAPException on validation exceptions
407 public static synchronized void validatePipConfiguration(Properties properties, StdPDPStatus status)
408 throws PAPException {
410 PIPFinderFactory factory = PIPFinderFactory.newInstance(properties);
411 if (factory == null) {
412 throw new FactoryException("Could not create PIP Finder Factory: "
413 + properties.getProperty(XACMLProperties.PROP_PIPFINDERFACTORY));
415 PIPFinder finder = factory.getFinder(properties);
417 // Check for this, although it should always return something
419 if (finder == null) {
420 LOGGER.error(XACMLErrorConstants.ERROR_PROCESS_FLOW + "pip finder factory returned a null engine.");
421 throw new PIPException("Could not create PIP Finder");
423 LOGGER.info("Loaded PIP finder");
425 for (PIPEngine engine : finder.getPIPEngines()) {
426 LOGGER.info("Configured PIP Engine: " + engine.getName());
427 StdPDPPIPConfig config = new StdPDPPIPConfig();
428 config.setName(engine.getName());
429 status.addLoadedPipConfig(config);
431 } catch (FactoryException | PIPException e) {
432 LOGGER.error(XACMLErrorConstants.ERROR_PROCESS_FLOW + "validate PIP configuration failed: "
433 + e.getLocalizedMessage());
434 status.addLoadError(e.getLocalizedMessage());
435 status.setStatus(Status.LOAD_ERRORS);
436 throw new PAPException(e);
441 * Iterates the policies defined in the props object to ensure they are loaded locally. Policies are searched for in
442 * the following order: - see if the current properties has a "<PolicyID>.file" entry and that file exists in
443 * the local directory - if not, see if the file exists in the local directory; if so create a ".file" property for
444 * it. - if not, get the "<PolicyID>.url" property and try to GET the policy from that location (and set the
447 * <p>If the ".file" property is created, then true is returned to tell the caller that the props object changed.
449 * @param props the properties to cache
450 * @return true/false if anything was changed in the props object
451 * @throws PAPException on cacheing exceptions
453 public static synchronized boolean cachePolicies(Properties props) throws PAPException {
454 boolean changed = false;
455 String[] lists = new String[2];
456 lists[0] = props.getProperty(XACMLProperties.PROP_ROOTPOLICIES);
457 lists[1] = props.getProperty(XACMLProperties.PROP_REFERENCEDPOLICIES);
458 for (String list : lists) {
460 // Check for a null or empty parameter
462 if (list == null || list.length() == 0) {
465 Iterable<String> policies = Splitter.on(',').trimResults().omitEmptyStrings().split(list);
466 for (String policy : policies) {
467 boolean policyExists = false;
469 // First look for ".file" property and verify the file exists
470 String propLocation = props.getProperty(policy + StdPolicyFinderFactory.PROP_FILE);
471 if (propLocation != null) {
475 policyExists = Paths.get(propLocation).toFile().exists();
477 LOGGER.warn(XACMLErrorConstants.ERROR_PROCESS_FLOW + "Policy file " + policy + " expected at "
478 + propLocation + DOES_NOT_EXIST);
482 // If ".file" property does not exist, try looking for the local
484 // (it might exist without having a ".file" property set for it)
487 // Now construct the output file name
489 Path outFile = Paths.get(getPDPConfig().toAbsolutePath().toString(), policy);
491 // Double check to see if we pulled it at some point
493 policyExists = outFile.toFile().exists();
496 // Set the property so the PDP engine doesn't have
497 // to pull it from the URL but rather the FILE.
499 LOGGER.info("Policy does exist: " + outFile.toAbsolutePath().toString());
500 props.setProperty(policy + StdPolicyFinderFactory.PROP_FILE,
501 outFile.toAbsolutePath().toString());
503 // Indicate that there were changes made to the
509 // File does not exist locally, so we need to get it
510 // from the location given in the ".url" property (which
514 // There better be a URL to retrieve it
516 propLocation = props.getProperty(policy + StdPolicyFinderFactory.PROP_URL);
517 if (propLocation != null) {
521 PapUrlResolver papUrls = PapUrlResolver.getInstance();
522 while (papUrls.hasMoreUrls()) {
523 String papID = papUrls.getUserId();
524 String papPass = papUrls.getPass();
525 Base64.Encoder encoder = Base64.getEncoder();
526 String encoding = encoder
527 .encodeToString((papID + ":" + papPass).getBytes(StandardCharsets.UTF_8));
533 url = new URL(papUrls.getUrl(PapUrlResolver.extractIdFromUrl(propLocation)));
534 LOGGER.info("Pulling " + url.toString());
536 // Open the connection
538 URLConnection urlConnection = url.openConnection();
539 urlConnection.setRequestProperty(XacmlRestProperties.PROP_PDP_HTTP_HEADER_ID,
540 XACMLProperties.getProperty(XacmlRestProperties.PROP_PDP_ID));
541 urlConnection.setRequestProperty("Authorization", "Basic " + encoding);
545 try (InputStream is = urlConnection.getInputStream();
546 OutputStream os = new FileOutputStream(outFile.toFile())) {
547 IOUtils.copy(is, os);
550 // Now save it in the properties as a .file
552 LOGGER.info("Pulled policy: " + outFile.toAbsolutePath().toString());
553 props.setProperty(policy + StdPolicyFinderFactory.PROP_FILE,
554 outFile.toAbsolutePath().toString());
557 // Indicate that there were changes made to the
561 } catch (MalformedURLException e) {
563 LOGGER.error(XACMLErrorConstants.ERROR_PROCESS_FLOW + "Policy '" + policy
564 + "' had bad URL in new configuration, URL='" + propLocation + "'");
565 } catch (Exception e) {
568 XACMLErrorConstants.ERROR_PROCESS_FLOW + "Error while retrieving policy "
569 + policy + " from URL " + url + ", e=" + e);
574 LOGGER.error(XACMLErrorConstants.ERROR_PROCESS_FLOW + "Policy " + policy
575 + " does NOT exist and does NOT have a URL");
585 * Get the PDP policy cache.
587 * @return the PDP policy cache
588 * @throws PAPException on cache get errors
590 public static synchronized Path getPDPPolicyCache() throws PAPException {
591 Path config = getPDPConfig();
592 Path policyProperties = Paths.get(config.toAbsolutePath().toString(), "xacml.policy.properties");
593 if (!policyProperties.toFile().exists()) {
594 LOGGER.warn(XACMLErrorConstants.ERROR_PROCESS_FLOW + policyProperties.toAbsolutePath().toString()
597 // Try to create the file
600 Files.createFile(policyProperties);
601 } catch (IOException e) {
602 LOGGER.error(XACMLErrorConstants.ERROR_PROCESS_FLOW + "Failed to create policy properties file: "
603 + policyProperties.toAbsolutePath().toString() + e);
604 throw new PAPException(
605 "Failed to create policy properties file: " + policyProperties.toAbsolutePath().toString());
608 return policyProperties;
612 * Get the PIP configuration.
614 * @return the PIP configuration
615 * @throws PAPException on get exceptions
617 public static synchronized Path getPIPConfig() throws PAPException {
618 Path config = getPDPConfig();
619 Path pipConfigProperties = Paths.get(config.toAbsolutePath().toString(), "xacml.pip.properties");
620 if (!pipConfigProperties.toFile().exists()) {
621 LOGGER.warn(XACMLErrorConstants.ERROR_PROCESS_FLOW + pipConfigProperties.toAbsolutePath().toString()
624 // Try to create the file
627 Files.createFile(pipConfigProperties);
628 } catch (IOException e) {
629 LOGGER.error(XACMLErrorConstants.ERROR_PROCESS_FLOW + "Failed to create pip properties file: "
630 + pipConfigProperties.toAbsolutePath().toString() + e);
631 throw new PAPException(
632 "Failed to create pip properties file: " + pipConfigProperties.toAbsolutePath().toString());
635 return pipConfigProperties;
639 * Get the PDP configuration.
641 * @return the PDP configuration
642 * @throws PAPException on get exceptions
644 public static synchronized Path getPDPConfig() throws PAPException {
645 Path config = Paths.get(XACMLProperties.getProperty(XacmlRestProperties.PROP_PDP_CONFIG));
646 if (!config.toFile().exists()) {
648 XACMLErrorConstants.ERROR_PROCESS_FLOW + config.toAbsolutePath().toString() + DOES_NOT_EXIST);
650 // Try to create the directory
653 Files.createDirectories(config);
654 } catch (IOException e) {
655 LOGGER.error(XACMLErrorConstants.ERROR_PROCESS_FLOW + "Failed to create config directory: "
656 + config.toAbsolutePath().toString(), e);
657 throw new PAPException("Failed to create config directory: " + config.toAbsolutePath().toString());