2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2017-2018 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.
18 * ============LICENSE_END=========================================================
21 package org.onap.policy.pdp.rest;
23 import java.io.FileOutputStream;
24 import java.io.IOException;
25 import java.io.InputStream;
26 import java.io.OutputStream;
27 import java.net.MalformedURLException;
30 import java.net.URLConnection;
31 import java.nio.charset.StandardCharsets;
32 import java.nio.file.Files;
33 import java.nio.file.Path;
34 import java.nio.file.Paths;
35 import java.util.Base64;
36 import java.util.ConcurrentModificationException;
37 import java.util.HashMap;
38 import java.util.Properties;
41 import org.apache.commons.io.IOUtils;
42 import org.onap.policy.pdp.rest.notifications.NotificationController;
43 import org.onap.policy.rest.XACMLRest;
44 import org.onap.policy.rest.XACMLRestProperties;
45 import org.onap.policy.common.logging.flexlogger.FlexLogger;
46 import org.onap.policy.common.logging.flexlogger.Logger;
48 import org.onap.policy.xacml.api.XACMLErrorConstants;
49 import com.att.research.xacml.api.pap.PAPException;
50 import com.att.research.xacml.api.pap.PDPStatus;
51 import com.att.research.xacml.api.pap.PDPStatus.Status;
52 import com.att.research.xacml.api.pdp.PDPEngine;
53 import com.att.research.xacml.api.pdp.PDPEngineFactory;
54 import com.att.research.xacml.api.pip.PIPEngine;
55 import com.att.research.xacml.api.pip.PIPException;
56 import com.att.research.xacml.api.pip.PIPFinder;
57 import com.att.research.xacml.api.pip.PIPFinderFactory;
58 import org.onap.policy.xacml.std.pap.StdPDPPIPConfig;
59 import org.onap.policy.xacml.std.pap.StdPDPPolicy;
60 import org.onap.policy.xacml.std.pap.StdPDPStatus;
61 import com.att.research.xacml.util.FactoryException;
62 import com.att.research.xacml.util.XACMLProperties;
63 import com.att.research.xacmlatt.pdp.policy.PolicyDef;
64 import com.att.research.xacmlatt.pdp.policy.dom.DOMPolicyDef;
65 import com.att.research.xacmlatt.pdp.std.StdPolicyFinderFactory;
66 import com.google.common.base.Splitter;
69 * Does the work for loading policy and PIP configurations sent from the PAP
75 public class XACMLPdpLoader {
76 private static final Logger LOGGER = FlexLogger.getLogger(XACMLPdpLoader.class);
77 private static NotificationController notificationController = new NotificationController();
78 private static final Long notifyDelay = (long) XACMLPdpServlet.getNotificationDelay();
81 public static synchronized PDPEngine loadEngine(StdPDPStatus status,
82 Properties policyProperties, Properties pipProperties) {
83 LOGGER.info("loadEngine: " + policyProperties + " " + pipProperties);
85 // First load our policies
89 // Were we given some properties?
91 if (policyProperties == null) {
93 // On init we have no incoming configuration, so just
94 // Load our current saved configuration
96 policyProperties = new Properties();
97 try (InputStream is = Files.newInputStream(getPDPPolicyCache())) {
98 policyProperties.load(is);
103 // Get our policy cache up-to-date
105 // Side effects of this include:
106 // - downloading of policies from remote locations, and
107 // - creating new "<PolicyId>.file" properties for files existing
110 LOGGER.info("XACMLPdpLoader: cache the policies.");
111 XACMLPdpLoader.cachePolicies(policyProperties);
113 // Validate the policies
115 LOGGER.info("XACMLPdpLoader: validating the policies.");
116 XACMLPdpLoader.validatePolicies(policyProperties, status);
117 if (LOGGER.isDebugEnabled()) {
118 LOGGER.debug("Status: " + status);
120 } catch (ConcurrentModificationException e) {
121 LOGGER.error(XACMLErrorConstants.ERROR_PROCESS_FLOW + e.getMessage() + e);
122 } catch (Exception e) {
123 String error = "Failed to load Policy Cache properties file: "
125 LOGGER.error(XACMLErrorConstants.ERROR_PROCESS_FLOW + error, e);
126 status.addLoadError(error);
127 status.setStatus(PDPStatus.Status.LOAD_ERRORS);
130 // Load our PIP configuration
134 // Were we given some properties to use?
136 if (pipProperties == null) {
138 // Load our current saved configuration
140 pipProperties = new Properties();
141 try (InputStream is = Files.newInputStream(getPIPConfig())) {
142 pipProperties.load(is);
146 // Validate our PIP configurations
148 XACMLPdpLoader.validatePipConfiguration(pipProperties, status);
149 if (LOGGER.isDebugEnabled()) {
150 LOGGER.debug("Status: " + status);
152 } catch (Exception e) {
153 String error = "Failed to load/validate Pip Config properties file: "
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;
199 public static synchronized void sendNotification(){
200 Thread notify = new Thread(){
203 Thread.sleep(notifyDelay);
204 NotificationController.sendNotification();
206 LOGGER.error(XACMLErrorConstants.ERROR_UNKNOWN + e);
213 public static synchronized void validatePolicies(Properties properties,
214 StdPDPStatus status) throws PAPException {
215 Set<String> rootPolicies = XACMLProperties.getRootPolicyIDs(properties);
216 Set<String> refPolicies = XACMLProperties
217 .getReferencedPolicyIDs(properties);
218 policyContainer = new HashMap<String, PolicyDef>();
220 LOGGER.info("XACMLPdpLoader: load rootPolicies");
221 for (String id : rootPolicies) {
222 loadPolicy(properties, status, id, true);
224 // remember which policies were root policies
225 status.addAllLoadedRootPolicies(status.getLoadedPolicies());
226 LOGGER.info("XACMLPdpLoader: load referencedPolicies");
227 for (String id : refPolicies) {
228 loadPolicy(properties, status, id, false);
230 LOGGER.info("Loaded " + status.getLoadedPolicies().size()
231 + " policies, failed to load "
232 + status.getFailedPolicies().size() + " policies, "
233 + status.getLoadedRootPolicies().size() + " root policies");
234 notificationController.check(status, policyContainer);
235 if (status.getLoadedRootPolicies().size() == 0) {
236 LOGGER.warn(XACMLErrorConstants.ERROR_PROCESS_FLOW +"NO ROOT POLICIES LOADED!!! Cannot serve PEP Requests.");
237 status.addLoadWarning("NO ROOT POLICIES LOADED!!! Cannot serve PEP Requests.");
239 policyContainer.clear();
243 public static synchronized void loadPolicy(Properties properties,
244 StdPDPStatus status, String id, boolean isRoot) throws PAPException {
245 PolicyDef policy = null;
246 String location = null;
247 URI locationURI = null;
248 boolean isFile = false;
249 boolean rougeFile = false;
251 location = properties.getProperty(id + ".file");
252 if(location != null){
254 locationURI = Paths.get(location).toUri();
255 try (InputStream is = Files.newInputStream(Paths.get(location))) {
256 policy = DOMPolicyDef.load(is);
257 } catch (Exception e){
258 // This Happens if a any issue with the error policyFile. Lets remove it.
260 LOGGER.error("Corrupted policy file, deleting: " + location + e);
261 Files.delete(Paths.get(location));
262 properties.remove(id + ".file");
264 } catch (IOException e1) {
269 if(location==null || rougeFile){
273 location = properties.getProperty(id + ".url");
274 if (location != null) {
279 boolean error= false;
282 PapUrlResolver papUrls = PapUrlResolver.getInstance();
283 while(papUrls.hasMoreUrls()){
284 String papID = papUrls.getUserId();
285 String papPass = papUrls.getPass();
286 Base64.Encoder encoder = Base64.getEncoder();
287 String encoding = encoder.encodeToString((papID+":"+papPass).getBytes(StandardCharsets.UTF_8));
288 locationURI = URI.create(papUrls.getUrl(PapUrlResolver.extractIdFromUrl(location)));
289 URL url = locationURI.toURL();
290 URLConnection urlConnection = null;
292 urlConnection = url.openConnection();
293 } catch (IOException e){
294 LOGGER.error("Exception Occured while opening connection" +e);
299 urlConnection.setRequestProperty(XACMLRestProperties.PROP_PDP_HTTP_HEADER_ID,
300 XACMLProperties.getProperty(XACMLRestProperties.PROP_PDP_ID));
301 urlConnection.setRequestProperty("Authorization", "Basic " + encoding);
303 // Now construct the output file name
305 Path outFile = Paths.get(getPDPConfig().toAbsolutePath()
310 try (FileOutputStream fos = new FileOutputStream(
312 IOUtils.copy(urlConnection.getInputStream(), fos);
313 } catch(IOException e){
314 LOGGER.error("Exception Occured while Copying input stream" +e);
323 try (InputStream fis = Files.newInputStream(outFile)) {
324 policy = DOMPolicyDef.load(fis);
327 LOGGER.error("Corrupted policy file, deleting: " + location +e);
328 Files.delete(outFile);
332 } catch (IOException e1) {
339 properties.setProperty(id + ".file", outFile
340 .toAbsolutePath().toString());
344 }while(error && errorCount>2);
347 if (policy != null) {
348 status.addLoadedPolicy(new StdPDPPolicy(id, isRoot,
349 locationURI, properties));
350 LOGGER.info("Loaded policy: " + policy.getIdentifier()
351 + " version: " + policy.getVersion().stringValue());
352 // Sending the policy objects to the Notification Controller.
353 policyContainer.put(id, policy);
355 String error = "Failed to load policy " + location;
356 LOGGER.error(XACMLErrorConstants.ERROR_PROCESS_FLOW + error);
357 status.setStatus(PDPStatus.Status.LOAD_ERRORS);
358 status.addLoadError(error);
359 status.addFailedPolicy(new StdPDPPolicy(id, isRoot));
361 } catch (Exception e) {
362 LOGGER.error(XACMLErrorConstants.ERROR_PROCESS_FLOW +"Failed to load policy '" + id + "' from location '"
363 + location + "'", e);
364 status.setStatus(PDPStatus.Status.LOAD_ERRORS);
365 status.addFailedPolicy(new StdPDPPolicy(id, isRoot));
374 LOGGER.error(XACMLErrorConstants.ERROR_PROCESS_FLOW + "Corrupted policy file, deleting: " + location);
375 Files.delete(Paths.get(location));
377 } catch (IOException e1) {
378 LOGGER.error(XACMLErrorConstants.ERROR_SYSTEM_ERROR + e1);
384 public static synchronized void validatePipConfiguration(
385 Properties properties, StdPDPStatus status) throws PAPException {
387 PIPFinderFactory factory = PIPFinderFactory.newInstance(properties);
388 if (factory == null) {
389 throw new FactoryException(
390 "Could not create PIP Finder Factory: "
392 .getProperty(XACMLProperties.PROP_PIPFINDERFACTORY));
394 PIPFinder finder = factory.getFinder(properties);
396 // Check for this, although it should always return something
398 if (finder == null) {
399 LOGGER.error(XACMLErrorConstants.ERROR_PROCESS_FLOW + "pip finder factory returned a null engine.");
400 throw new PIPException("Could not create PIP Finder");
402 LOGGER.info("Loaded PIP finder");
404 for (PIPEngine engine : finder.getPIPEngines()) {
405 LOGGER.info("Configured PIP Engine: " + engine.getName());
406 StdPDPPIPConfig config = new StdPDPPIPConfig();
407 config.setName(engine.getName());
408 status.addLoadedPipConfig(config);
410 } catch (FactoryException | PIPException e) {
411 LOGGER.error(XACMLErrorConstants.ERROR_PROCESS_FLOW + "validate PIP configuration failed: "
412 + e.getLocalizedMessage());
413 status.addLoadError(e.getLocalizedMessage());
414 status.setStatus(Status.LOAD_ERRORS);
415 throw new PAPException(e);
420 * Iterates the policies defined in the props object to ensure they are
421 * loaded locally. Policies are searched for in the following order: - see
422 * if the current properties has a "<PolicyID>.file" entry and that
423 * file exists in the local directory - if not, see if the file exists in
424 * the local directory; if so create a ".file" property for it. - if not,
425 * get the "<PolicyID>.url" property and try to GET the policy from
426 * that location (and set the ".file" property)
428 * If the ".file" property is created, then true is returned to tell the
429 * caller that the props object changed.
432 * @return true/false if anything was changed in the props object
433 * @throws PAPException
435 public static synchronized boolean cachePolicies(Properties props)
436 throws PAPException {
437 boolean changed = false;
438 String[] lists = new String[2];
439 lists[0] = props.getProperty(XACMLProperties.PROP_ROOTPOLICIES);
440 lists[1] = props.getProperty(XACMLProperties.PROP_REFERENCEDPOLICIES);
441 for (String list : lists) {
443 // Check for a null or empty parameter
445 if (list == null || list.length() == 0) {
448 Iterable<String> policies = Splitter.on(',').trimResults()
449 .omitEmptyStrings().split(list);
450 for (String policy : policies) {
451 boolean policyExists = false;
453 // First look for ".file" property and verify the file exists
454 String propLocation = props.getProperty(policy
455 + StdPolicyFinderFactory.PROP_FILE);
456 if (propLocation != null) {
460 policyExists = Files.exists(Paths.get(propLocation));
461 if (policyExists == false) {
462 LOGGER.warn(XACMLErrorConstants.ERROR_PROCESS_FLOW + "Policy file " + policy + " expected at "
463 + propLocation + " does NOT exist.");
467 // If ".file" property does not exist, try looking for the local
469 // (it might exist without having a ".file" property set for it)
470 if (policyExists == false) {
472 // Now construct the output file name
474 Path outFile = Paths.get(getPDPConfig().toAbsolutePath()
475 .toString(), policy);
477 // Double check to see if we pulled it at some point
479 policyExists = Files.exists(outFile);
482 // Set the property so the PDP engine doesn't have
483 // to pull it from the URL but rather the FILE.
485 LOGGER.info("Policy does exist: "
486 + outFile.toAbsolutePath().toString());
487 props.setProperty(policy
488 + StdPolicyFinderFactory.PROP_FILE, outFile
489 .toAbsolutePath().toString());
491 // Indicate that there were changes made to the
497 // File does not exist locally, so we need to get it
498 // from the location given in the ".url" property (which
502 // There better be a URL to retrieve it
504 propLocation = props.getProperty(policy
505 + StdPolicyFinderFactory.PROP_URL);
506 if (propLocation != null) {
510 PapUrlResolver papUrls = PapUrlResolver.getInstance();
511 while(papUrls.hasMoreUrls()){
512 String papID = papUrls.getUserId();
513 String papPass = papUrls.getPass();
514 Base64.Encoder encoder = Base64.getEncoder();
515 String encoding = encoder.encodeToString((papID+":"+papPass).getBytes(StandardCharsets.UTF_8));
521 url = new URL(papUrls.getUrl(PapUrlResolver.extractIdFromUrl(propLocation)));
522 LOGGER.info("Pulling " + url.toString());
524 // Open the connection
526 URLConnection urlConnection = url.openConnection();
527 urlConnection.setRequestProperty(XACMLRestProperties.PROP_PDP_HTTP_HEADER_ID,
528 XACMLProperties.getProperty(XACMLRestProperties.PROP_PDP_ID));
529 urlConnection.setRequestProperty("Authorization", "Basic " + encoding);
533 try (InputStream is = urlConnection
535 OutputStream os = new FileOutputStream(
537 IOUtils.copy(is, os);
540 // Now save it in the properties as a .file
542 LOGGER.info("Pulled policy: "
543 + outFile.toAbsolutePath().toString());
544 props.setProperty(policy
545 + StdPolicyFinderFactory.PROP_FILE,
546 outFile.toAbsolutePath().toString());
549 // Indicate that there were changes made to the
553 } catch (MalformedURLException e) {
555 LOGGER.error(XACMLErrorConstants.ERROR_PROCESS_FLOW + "Policy '" + policy
556 + "' had bad URL in new configuration, URL='" + propLocation + "'");
557 } catch (Exception e) {
559 LOGGER.error(XACMLErrorConstants.ERROR_PROCESS_FLOW + "Error while retrieving policy "
560 + policy + " from URL " + url + ", e=" + e);
565 LOGGER.error(XACMLErrorConstants.ERROR_PROCESS_FLOW + "Policy " + policy
566 + " does NOT exist and does NOT have a URL");
575 public static synchronized Path getPDPPolicyCache() throws PAPException {
576 Path config = getPDPConfig();
577 Path policyProperties = Paths.get(config.toAbsolutePath().toString(),
578 "xacml.policy.properties");
579 if (Files.notExists(policyProperties)) {
580 LOGGER.warn(XACMLErrorConstants.ERROR_PROCESS_FLOW + policyProperties.toAbsolutePath().toString()
581 + " does NOT exist.");
583 // Try to create the file
586 Files.createFile(policyProperties);
587 } catch (IOException e) {
588 LOGGER.error(XACMLErrorConstants.ERROR_PROCESS_FLOW + "Failed to create policy properties file: "
589 + policyProperties.toAbsolutePath().toString() +e);
590 throw new PAPException(
591 "Failed to create policy properties file: "
592 + policyProperties.toAbsolutePath().toString());
595 return policyProperties;
598 public static synchronized Path getPIPConfig() throws PAPException {
599 Path config = getPDPConfig();
600 Path pipConfigProperties = Paths.get(
601 config.toAbsolutePath().toString(), "xacml.pip.properties");
602 if (Files.notExists(pipConfigProperties)) {
603 LOGGER.warn(XACMLErrorConstants.ERROR_PROCESS_FLOW + pipConfigProperties.toAbsolutePath().toString()
604 + " does NOT exist.");
606 // Try to create the file
609 Files.createFile(pipConfigProperties);
610 } catch (IOException e) {
611 LOGGER.error(XACMLErrorConstants.ERROR_PROCESS_FLOW + "Failed to create pip properties file: "
612 + pipConfigProperties.toAbsolutePath().toString() +e);
613 throw new PAPException("Failed to create pip properties file: "
614 + pipConfigProperties.toAbsolutePath().toString());
617 return pipConfigProperties;
620 public static synchronized Path getPDPConfig() throws PAPException {
621 Path config = Paths.get(XACMLProperties
622 .getProperty(XACMLRestProperties.PROP_PDP_CONFIG));
623 if (Files.notExists(config)) {
624 LOGGER.warn(XACMLErrorConstants.ERROR_PROCESS_FLOW + config.toAbsolutePath().toString() + " does NOT exist.");
626 // Try to create the directory
629 Files.createDirectories(config);
630 } catch (IOException e) {
631 LOGGER.error(XACMLErrorConstants.ERROR_PROCESS_FLOW + "Failed to create config directory: "
632 + config.toAbsolutePath().toString(), e);
633 throw new PAPException("Failed to create config directory: "
634 + config.toAbsolutePath().toString());